Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is std::views::all introduced for in C++20?

#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?

like image 705
xmllmx Avatar asked Sep 11 '25 04:09

xmllmx


2 Answers

But I cannot tell any difference between v | std::views::drop(2) and std::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 expression views​::​all(E) is expression-equivalent to:

  • decay-copy(E) if the decayed type of E models view.
  • 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.

like image 161
Barry Avatar answered Sep 13 '25 20:09

Barry


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!
   ...
}
like image 37
MarkB Avatar answered Sep 13 '25 22:09

MarkB