Reputation: 42343
#include <vector>
#include <ranges>
int main()
{
auto v = std::vector{1, 2, 3, 4};
v | std::views::drop(2); // ok
std::views::all(v) | std::views::drop(2); // also ok
}
Successfully compiled with g++11 -std=c++20
. But I cannot tell any difference between v | std::views::drop(2)
and std::views::all(v) | std::views::drop(2)
.
So, my question is:
What is std::views::all
introduced for in C++20?
Upvotes: 25
Views: 14091
Reputation: 4896
std::views::all
can act as an intermediate layer to avoid unnecessary copies when dealing some class templates which cannot treat references properly. An example is the use of std::initializer_list<T>
where T
is a std::views::all
over an existing container. It can be useful to implement something functional similar to std::views::concat
in C++26. An example code is shown below,
#include <vector>
#include <iostream>
#include <ranges>
struct A{
A(int i):i(i){}
A(const A& other){
std::cout << "Copy\n";
i = other.i;
}
A(A&& other){
std::cout << "Move\n";
i = other.i;
}
A& operator=(const A& other){
std::cout << "Copy assignment\n";
i = other.i;
return *this;
}
A& operator=(A&& other){
std::cout << "Move assignment\n";
i = other.i;
return *this;
}
int i;
};
int main(){
std::vector<A> avec1{1,2,3};
std::vector<A> avec2{4,5,6};
std::cout << "After vector construction\n";
auto vecs = {std::views::all(avec1), std::views::all(avec2)};
for(const auto& el: std::views::join(vecs)){
std::cout << el.i << ",";
}
std::cout << '\n';
}
Here std::views::all
is used to provide a way to iterate over the underlying vector without unnecessary copies. std::views::all
is necessary because auto vecs={avec1, avec2};
would copy both avec1
and avec2
and std::initializer_list<T>
doesn't work when T
is a reference-type. Another choice here is std::span
, but std::views::all
has the benefit of also working for other types of containers.
Note that the underlying types of the initializer_list
still need to be of the same type in this case and hence std::views::concat
in C++26 is still necessary to handle different types of views.
Demo: https://godbolt.org/z/aj16a16f6
Upvotes: 0
Reputation: 1988
You may want your interface to return a range instead of the underlying container. In the example below, container_api
exposes member methods (i.e. of std::vector) that aren't part of a view (e.g. rbegin(), capacity(), max_size()). range_api
exposes operator bool
, which isn't part of a vector.
Another important difference is that the return type of range_api
is an object and not a reference. This may prevent unintentional copies from users thinking they are getting a range when the actual interface is returning a reference to a container.
class Foo {
public:
Foo() { ... }
const auto& container_api() const { return m_vec; }
auto range_api() const { return std::views::all(m_vec); }
private:
std::vector<int> m_vec;
};
void some_fn(const Foo& foo)
{
auto rng = foo.container_api(); // unwanted copy!
...
}
Upvotes: 3
Reputation: 303057
But I cannot tell any difference between
v | std::views::drop(2)
andstd::views::all(v) | std::views::drop(2)
.
Indeed, there is no difference between the two - because v | views::drop(2)
already means views::all(v) | views::drop(2)
.
views::all
is an implementation detail of Ranges to ensure that range adaptors always adapt views (not ranges). All that views::all(v)
does is ensure that the result is a View, which is to say (from [range.all]):
Given a subexpression
E
, the expressionviews::all(E)
is expression-equivalent to:
decay-copy(E)
if the decayed type ofE
modelsview
.- Otherwise,
ref_view{E}
if that expression is well-formed.- Otherwise,
subrange{E}
.
In your case, v
is a vector<int>
, which does not model view
. But it is an lvalue, so ref_view{v}
would be well-formed, so that's what happens.
All the adaptors use views::all
internally. For instance, drop_view
has the following deduction guide:
template <class R>
drop_view(R&&, range_difference_t<R>) -> drop_view<views::all_t<R>>;
So if you wrote drop_view(v, 2)
(and you should never use meow_view
directly, always use views::meow
), that would itself invoke views::all
for you.
Upvotes: 28