How to use std::transform with container without push_back?

Viewed 1360

I have a container that has no push_back() method. But this container has an iterator on begin()and end().

I would like to use std::transform() to output into that container. But std::back_inserter needs to call push_back() on the output container.

Is it possible to use std::transform() to output into a container that just supports direct assignment? Like:

for (auto item : containerNoPushBack)
{
    item = calculateValue();
}

Or indexed assignment like:

for (size_t i = 0; i < containerNoPushBack.size(); ++i)
{
    item[i] = calculateValue();
}
3 Answers

If you can do item[i] (i.e. your container has enough elements in it), then you can simply use your_container.begin() in std::transform, no need for std::back_inserter. std::back_inserter is just a nice way to avoid explicitly resizing containers before applying it.

The following snippets will both fill my_output with contents of my_input:

std::vector<int> my_output;
std::transform(my_input.begin(), my_input.end(), std::back_inserter(my_output), [](const auto& arg){return arg;});

std::vector<int> my_output;
my_output.resize(my_input.size());
std::transform(my_input.begin(), my_input.end(), my_output.begin(), [](const auto& arg){return arg;});

std::transform does use "direct assignment". only if you need to push the elements you would use a back_inserter, if the target has already elements you dont need that.

Consider the possible implementation (taken from cppreference):

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first, 
                   UnaryOperation unary_op)
{
    while (first1 != last1) {
        *d_first++ = unary_op(*first1++);
    }
    return d_first;
}

std::transform itself does not push anything. Exactly for that reason you need a back_inserter. However, instead you can resize the target to have enough space for the transformed elements.

Just use a plain std::transform with begin and end iterators. No need for std::back_inserter or even an std::inserter.

For example, std::array does not support the push_back method, but std::transform works on it:

#include <iostream>
#include <array>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5};

    std::array<int, 5> arr{};

    // note the lack of std::back_inserter just below
    std::transform(data.cbegin(), data.cend(), arr.begin(),
            [](auto i) {
                return i * i;
            });

    for (const auto i : arr) {
        std::cout << i << ' '; // prints 1, 4, 9, 16, 25
    }
}
Related