Range view to std::vector
Asked Answered
U

3

25

In the proposed C++20 (The One) Ranges TS, what is the proposed method for converting the view into a std::vector?

The following code does not compile:

int                                           
main() {                                                        
    std::vector<float> values = {1.0, 2.0, 3.0, 4.0, 5.2, 6.0, 7.0, 8.0, 9.0}; 
    //fmt::print("{}\n", std::experimental::ranges::views::filter(values, [] (float v) { return v < 5.f; })); 
    std::vector<float> foo = vw::filter(values, [] (float v) { return v < 5.f; }); 
    fmt::print("{}\n", foo);                
}

with the error

../src/view.cpp:19:40: error: conversion from     ‘std::experimental::ranges::v1::filter_view<std::experimental::ranges::v1::ref_view<std::vector<float> >, main()::<lambda(float)> >’ to non-scalar type ‘std::vector<float>’ requested
     std::vector<float> foo = vw::filter(values, [] (float v) { return v < 5.f; }); 

(the commented line will also not compile due to some CV constraints).

So how do I do anything with a view except for using a range-based for loop?

Also some bonus questions:

  1. Is the cmcstl2 implementation I used even following the proposal? The ranges-v3 seems not to be.
  2. Is there any documentation on the Ranges TS? The proposal PDF I found is pretty much an awfully formatted code dump in diff style. In fact directly reading the cmcstl2 sources was way easier to read for me. The cppreference seems to be lacking as well...
Unintelligent answered 11/11, 2019 at 20:9 Comment(7)
So how do I do anything with a view except for using a range-based for loop depends on what you want to do. Without knowing that we really can't suggest what to do.Broadleaved
What about using copy? It has new definitions in C++20: eel.is/c++draft/alg.copyMaxie
So how do I do anything with a view except for using a range-based for loop? Simply said: What more do you need? You can do pretty much whatever you want by iterating over the view. But the stl gives you indeed more options: Most of the stl-algorithms have a variant for ranges (and I think there are some which were not possible for iterators). Furthermore there is the really nice | operator to apply views on ranges.Peaslee
Creating an empty container and using std::copy is indeed worth a try. Just wondered why both, assignment to containers as well as ranges::to<Container> got dropped w.r.t. the ranges-v3. To those that wonder what I want to do: I want to manifest the lazily evaluated view into an actual container - i.e. exactly what is done in the code snippet.Unintelligent
Can you link an online compiler that with a minimal reproducible example for us to start with?Keldon
I just checked half a dozen online compilers and only one (wandbox) had a ranges implementation and it failed to include it due to an unknown type. If you can point me to a compiler that has a working implementation I am happy to provide that. The code above should work though with g++ >9.1.0 by including <vector> and <experimental/ranges/ranges> (and having the cmcstl2 in include path ofc).Unintelligent
Niebler's ranges-v3 has to_vector. E.g.: github.com/ericniebler/range-v3/blob/master/test/range/… of course I'm not sure what was finally standardised...Eyeglass
E
31

The C++20 method to convert a view to a std::vector (or indeed any other container) is to pass the range's begin and end members to the vector constructor that accepts 2 iterators (and an optional allocator).

I was also looking for an answer to this question. What I really wanted is an overload of the constructor of std::vector accepting a range. Approximately:

template <std::ranges::input_range R>
vector(R&& r) : vector(r.begin(), r.end()) {
}

but that isn't in C++20.

First, I implemented this:

namespace rng = std::ranges;

template <rng::range R>
constexpr auto to_vector(R&& r) {
    using elem_t = std::decay_t<rng::range_value_t<R>>;
    return std::vector<elem_t>{r.begin(), r.end()};
}

which works, but isn't very "rangy": https://godbolt.org/z/f2xAcd

I then did it a bit better:

namespace detail {
    // Type acts as a tag to find the correct operator| overload
    template <typename C>
    struct to_helper {
    };
    
    // This actually does the work
    template <typename Container, rng::range R>
    requires std::convertible_to<rng::range_value_t<R>, typename Container::value_type>
    Container operator|(R&& r, to_helper<Container>) {
        return Container{r.begin(), r.end()};
    }
}

// Couldn't find an concept for container, however a
// container is a range, but not a view.
template <rng::range Container>
requires (!rng::view<Container>)
auto to() {
    return detail::to_helper<Container>{};
}

https://godbolt.org/z/G8cEGqeq6

No doubt one can do better for sized_ranges and containers like std::vector that have a reserve member function.

There is a proposal to add a to function to C++23 (https://wg21.link/p1206) which will do a better job than this, I'm sure.

Eyeglass answered 1/4, 2020 at 13:21 Comment(2)
for MSVC users: godbolt.org/z/8bcn8qh8fFlor
Did you have to change the code at all? (Sorry - I've never used MSVC)Eyeglass
E
17

C++23 finally provided an official solution: std::ranges::to.

// pipeline style
auto vec = view | std::ranges::to<std::vector>();

// function style
auto vec = std::ranges::to<std::vector>(view);

Presently, compiler support is lacking. GCC 14, to be released in 2024, is expected to include its support. This feature is already working in GCC 14's development version.

Earpiercing answered 22/3 at 17:51 Comment(0)
C
0

IMO problem here is incorrect use of fmt library. In presented example you do not need to do conversion to the std::vector you can feed range (view here) into fmt function:

int main()
{
    std::vector<float> values = { 1.0, 2.0, 3.0, 4.0, 5.2, 6.0, 7.0, 8.0, 9.0 };
    auto foo = std::views::filter(values, [](float v) { return v < 5.f; });
    fmt::print("[{}]\n", fmt::join(foo, ", "));
}

So no need to use C++23, just leverage functionality of fmt library.

https://godbolt.org/z/v9qnEnY7o

Chiropractor answered 26/3 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.