range-v3: What is the idiomatic way to store a range into an existing container?

74 views Asked by At

I have a range based on a forward iterator, so the size isn't known before encountering the end iterator. This iterator is used for a transformation with range-v3:

auto src = GetForwardIteratorRange();
auto dest = src | rv::transform([](auto item) { ... }) | r::to<vector>;

If I want to do dest.reserve(100'000) first, is there a way I can fill this vector later idiomatically with range-v3? Like doing r::to(dest) (which doesn't work)?

Edit: I'm not even sure

auto src = GetForwardIteratorRange();
auto transform = src | rv::transform([](auto item) { ... });
vector dest;
dest.reserve(100'000);
dest.assign(transform.begin(), transform.end());

would work. The cppreference page on vector::assign sounds as if the implementation can just throw any allocated space away ("The past-the-end iterator is also invalidated.")

I could probably use

boost::ranges::copy(transform, back_inserter(dest));

or

boost::ranges::transform(src, back_inserter(dest), [](auto item) {...});

but I'm wondering if I'm overlooking some range-v3 feature here.

1

There are 1 answers

2
Caleth On

Your reserve + assign code is fine.

The reason that it says all iterators are invalidated is because it destroys all the exisiting elements, then fills the empty space with the new elements, which in general might mean an allocation, but in your case won't, if your estimate is good.

ranges::to accepts a pack of arguments to pass to the constructor besides the iterators, so you could write a wrapper type that reserved space in a vector, then assigned into it.

template <typename T, typename A = std::allocator<T>>
class reserving_vector {
    std::vector<T, A> vector;
public:
    template <std::ranges::input_range R>
    reserving_vector(std::ranges::from_range_t, R range, size_t reservation) {
        vector.reserve(reservation);
        vector.assign(begin(range), end(range));
    }
    operator std::vector<T, A>() && { return std::move(vector); }
};

template <std::ranges::input_range R, typename A = std::allocator<std::ranges::range_value_t<R>>>
reserving_vector(std::ranges::from_range_t, R, size_t) -> reserving_vector<std::ranges::range_value_t<R>, A>;

auto src = GetForwardIteratorRange();
vector dest = src | rv::transform([](auto item) { ... }) | r::to<reserving_vector>(100'000);