Construct new container from transformed range

547 views Asked by At

In my code, I frequently have to create a new container from a previously transformed range. So far I have used a combination of boost::adaptors::transformed and boost::copy_range to do the job, thinking that the container's constructor should be able to preallocate the necessary memory. Unfortunately, I have noticed that boost::adaptors::transform returns a SinglePassRange and I'm not sure if the size of the range can be determined in constant time.

namespace boost {
    template <typename SeqT, typename Range>
    inline SeqT copy_range(const Range& r)
    {
        return SeqT(boost::begin(r), boost::end(r));
    }
}

auto ints = std::vector<int>{...};
auto strings = boost::copy_range<std::vector<std::string>>(
    boost::adaptors::transform(ints, [](auto val) { 
        return std::to_string(val); 
    }));

So my question is: What is the best generic way to construct a new container from a transformed range ideally as one expression?

2

There are 2 answers

1
sehe On BEST ANSWER

You can use boost::adaptors::transformed. The docs state that the input range MUST at least be SinlgePassRange, but also says:

  • Returned Range Category: The range category of rng.

SO if the input range is random-access the output range will be, too. This removes your worry.

4
AndreiM On

std::transform is the way to go in C++11 and above:

std::set<int> ints = {1,2,3,1};
std::vector<std::string> strings;
std::transform(ints.begin(), ints.end(), std::back_inserter(strings), [] (int i) {
    return std::to_string(i)});

Using this you can only apply transform to every element in the given range. If you also want to do some filtering, you can use std::copy_if. I recommend to study the header, it's full of gems.

EDIT: added back_inserter