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

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • For C++14/17 and C++20 that does not have `__cpp_lib_ranges` feature — like mine (/cry) — you can use Eric Niebler's Range-v3 `#include `, and use `ranges::views::all` and `range::views::take`. I think in the example `std::views::all(v)` is just being explicit about the vector being used as-a `view`. For a range-savvy code base I'd omit it. – Eljay Apr 30 '21 at 14:26

2 Answers2

20

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • So it's there to prevent you from using prvalue ranges that contain their elements in view compositions, unless you explicitly request it. – Nicol Bolas Apr 30 '21 at 14:51
  • @Barry sorry I meant it as a follow-up to the other comment, but it is irrelevant and I deleted it. My bad! – Sam Aug 06 '21 at 20:53
  • 3
    With the introduction of C++23 `owning_view`, I think this answer can be updated. In addition, I think `views::all` should be more *specialized* in some cases, that is, when `E` is `string&`, `views::all(E)` should be `string_view`, and when `E` is `contiguous_range&`, `view::all(E)` should be `span`, because they are more convenient for users than `ref_view`. – 康桓瑋 Dec 26 '21 at 16:51
  • 1
    @康桓瑋 `string&` and `string_view` give you different kinds of elements, so `views::all(E)` on a `string&` had really better not give you a `string_view`, that is totally wrong. – Barry Dec 26 '21 at 17:21
  • 1
    @Barry Oops, you are right, sometimes `view`s can be modified... – 康桓瑋 Dec 26 '21 at 17:28
  • 1
    @康桓瑋 `owning_view` is listed by cppreference as existing since C++20. That said, it's not clear to me what _it_ exists for, much less how that relates to `views::all` :-) Is it intended as another implementation detail or ranges, or a quick way to make any range type into a move-only version of itself? – underscore_d Jan 21 '22 at 10:00
  • 3
    @underscore_d. see [*What is a view?*](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2415r2.html) – 康桓瑋 Jan 21 '22 at 10:03
  • @康桓瑋 Thanks. I saw just before your reply that now `owning_view` is documented by cppreference as the last-ditch expression-equivalence for `views::all`, replacing `subrange`, so it exists to enable viewing rvalue ranges. I may ponder if I can find a use of `owning_view` as a general (not too evil?) way to make a copyable range type move-only! – underscore_d Jan 21 '22 at 10:07
  • Was P2415 a DR applied to C++20, or is it only for C++23 making cppreference wrong? My g++/libstdc++ does not have `owning_view`. **edit**: It seems [the former is true](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/n4902.html) – underscore_d Jan 21 '22 at 23:15
  • [Today I Learned](https://brevzin.github.io/c++/2023/03/14/prefer-views-meow/#viewsmeow-is-the-algorithm) that `meow` above is a [metasyntactic variable](https://en.wikipedia.org/wiki/Metasyntactic_variable). In other words, it is a [placeholder](https://www.reddit.com/r/cpp/comments/11ramwj/comment/jc94yxj/) (other common ones include `foo` and `bar`). In the specific above it means instead of `drop_view(v, 2)` use `views::drop(v, 2)`. Similarly for other adapters such as `take` instead of `take_view(v, 2)` use `views::take(v, 2)`. – John Cummings May 11 '23 at 15:10
2

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!
   ...
}
MarkB
  • 672
  • 2
  • 9