Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++20 ranges view to vector

Tags:

c++

c++20

Now that the C++20 ranges implementation is actually here and released under GCC 10.2, I would like to know how to convert a ranges view back to an actual container, like a vector. I've found this question ( Range view to std::vector ) that asked the same thing for the pre-release version but I would like to know if since its been released, have there been any new methods made to convert from a view to a container? Or is that single answer on that question still the best solution?

like image 704
Foxie Avatar asked Jul 27 '20 13:07

Foxie


People also ask

What are C ++ 20 ranges?

ranges::range. (C++20) specifies that a type is a range, that is, it provides a begin iterator and an end sentinel. (concept)

What is a C++ range?

Range - Ranges are an abstraction that allows a C++ program to operate on elements of data structures uniformly. On minimum a range contains defines begin() and end() to elements. There are several different types of ranges: containers, views, sized ranges, Container - It's a range that owns the elements.


1 Answers

Easiest thing to do would be to use range-v3, which has a conversion operator exactly for this. From the examples:

using namespace ranges;
auto vi =
    views::for_each(views::ints(1, 10), [](int i) {
        return yield_from(views::repeat_n(i, i));
    })
  | to<std::vector>();
// vi == {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,...}

Otherwise, the answer in the linked question isn't entirely accurate since a range may not have the same iterator and sentinel types, and the answer requires it. So we can do a little bit better:

template <std::ranges::range R>
auto to_vector(R&& r) {
    std::vector<std::ranges::range_value_t<R>> v;

    // if we can get a size, reserve that much
    if constexpr (requires { std::ranges::size(r); }) {
        v.reserve(std::ranges::size(r));
    }

    // push all the elements
    for (auto&& e : r) {
        v.push_back(static_cast<decltype(e)&&>(e));
    }

    return v;
}

A shorter version of the above, which doesn't necessarily reserve up front in all the same places, addresses the mixed-sentinel-type issue by using views::common:

template <std::ranges::range R>
auto to_vector(R&& r) {
    auto r_common = r | std::views::common;
    return std::vector(r_common.begin(), r_common.end());
}

The canonical example of missed reserve here would be invoking to_vector() with a std::list<T> - which has an O(1) size() available, which could be used to reserve, but we lose that once we go into the iterators.

like image 126
Barry Avatar answered Sep 19 '22 07:09

Barry