Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::views::split copies the pattern instead of making a reference

Tags:

c++

std-ranges

According to cppreference, std::views::split call signature (https://en.cppreference.com/w/cpp/ranges/split_view ):

template< ranges::viewable_range R, class Pattern >
requires /* see below */
constexpr ranges::view auto split( R&& r, Pattern&& pattern );

Those pattern is passed by reference. But then why doesn't it compile:

std::vector  a{ 1,2,-1,-2,3,4,-1,-2,5,6,7,-1,-2,8 };
std::vector  delimiter{ -1, -2 };
auto         sv = a  | std::views::split(delimiter);
auto         jv = sv | std::views::join; //Error

And for the above code fragment to work, you have to manually wrap pattern using ref_view:

std::vector  a{ 1,2,-1,-2,3,4,-1,-2,5,6,7,-1,-2,8 };
std::vector  delimiter{ -1, -2 };
auto         sv = a  | 
std::views::split(std::ranges::ref_view(delimiter));
auto         jv = sv | std::views::join; //Ok

It seems that std::views::split doesn't use a reference to pattern, but creates a copy of pattern. What exactly is happening here?

like image 493
Pavel Avatar asked Sep 01 '25 20:09

Pavel


1 Answers

The reason is that a | std::views::split(delimiter) is not the same as std::views::split(a, delimiter). Going via a RangeAdaptorObject means you need to store a copy of delimiter, which you then use as a rvalue. The deduction guide for std::ranges::split_view uses std::ranges::all_t, which chooses std::views::owning_view for rvalues, instead of std::views::ref_view, which it chooses for lvalues.

when the call is valid, its result object stores a subobject of type std::decay_t<E> direct-non-list-initialized with std::forward<E>(e), for every argument e in args... (in other words, range adaptor objects bind arguments by value),

calling the RangeAdaptorClosureObject forwards the bound arguments (if any) to the associated range adaptor object. The bound arguments (if any) are considered to have the value category and cv-qualification of the RangeAdaptorClosureObject.

It also compiles in the following situations, either because the adaptor is an lvalue, or we move from the owning_view

1.

auto sv = std::views::split(delimiter);
auto split = a | sv;
auto jv = split | std::views::join; // OK
auto sv = std::views::split(delimiter);
auto jv = a | sv | std::views::join; // OK
auto sv = a  | std::views::split(delimiter);
auto jv = std::move(sv) | std::views::join; // OK
like image 82
Caleth Avatar answered Sep 05 '25 21:09

Caleth