24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

This subclause defines range adaptors, which are utilities that transform a range into a view with custom behaviors.
These adaptors can be chained to create pipelines of range transformations that evaluate lazily as the resulting view is iterated.
Range adaptors are declared in namespace std​::​ranges​::​views.
The bitwise OR operator is overloaded for the purpose of creating adaptor chain pipelines.
The adaptors also support function call syntax with equivalent semantics.
Example
:
vector<int> ints{0,1,2,3,4,5};
auto even = [](int i){ return 0 == i % 2; };
auto square = [](int i) { return i * i; };
for (int i : ints | views::filter(even) | views::transform(square)) {
  cout << i << ' '; // prints: 0 4 16
}
assert(ranges::equal(ints | views::filter(even), views::filter(ints, even)));
— end example
 ]

24.7.1 Range adaptor objects [range.adaptor.object]

A range adaptor closure object is a unary function object that accepts a viewable_­range argument and returns a view.
For a range adaptor closure object C and an expression R such that decltype((R)) models viewable_­range, the following expressions are equivalent and yield a view:
C(R)
R | C
Given an additional range adaptor closure object D, the expression C | D is well-formed and produces another range adaptor closure object such that the following two expressions are equivalent:
R | C | D
R | (C | D)
A range adaptor object is a customization point object ([customization.point.object]) that accepts a viewable_­range as its first argument and returns a view.
If a range adaptor object accepts only one argument, then it is a range adaptor closure object.
If a range adaptor object accepts more than one argument, then the following expressions are equivalent:
adaptor(range, args...)
adaptor(args...)(range)
range | adaptor(args...)
In this case, adaptor(args...) is a range adaptor closure object.

24.7.2 Semiregular wrapper [range.semi.wrap]

Many types in this subclause are specified in terms of an exposition-only class template semiregular-box.
semiregular-box<T> behaves exactly like optional<T> with the following differences:
  • semiregular-box<T> constrains its type parameter T with copy_­constructible<T> && is_­object_­v<T>.
  • If T models default_­constructible, the default constructor of semiregular-box<T> is equivalent to:
    constexpr semiregular-box() noexcept(is_nothrow_default_constructible_v<T>)
      : semiregular-box{in_place}
    { }
    
  • If assignable_­from<T&, const T&> is not modeled, the copy assignment operator is equivalent to:
    semiregular-box& operator=(const semiregular-box& that)
      noexcept(is_nothrow_copy_constructible_v<T>)
    {
      if (that) emplace(*that);
      else reset();
      return *this;
    }
    
  • If assignable_­from<T&, T> is not modeled, the move assignment operator is equivalent to:
    semiregular-box& operator=(semiregular-box&& that)
      noexcept(is_nothrow_move_constructible_v<T>)
    {
      if (that) emplace(std::move(*that));
      else reset();
      return *this;
    }
    

24.7.3 All view [range.all]

views​::​all returns a view that includes all elements of its range argument.
The name views​::​all denotes a range adaptor object ([range.adaptor.object]).
For some 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}.

24.7.3.1 Class template ref_­view [range.ref.view]

ref_­view is a view of the elements of some other range.
namespace std::ranges {
  template<range R>
    requires is_object_v<R>
  class ref_view : public view_interface<ref_view<R>> {
  private:
    R* r_ = nullptr;            // exposition only
  public:
    constexpr ref_view() noexcept = default;

    template<not-same-as<ref_view> T>
      requires see below
    constexpr ref_view(T&& t);

    constexpr R& base() const { return *r_; }

    constexpr iterator_t<R> begin() const { return ranges::begin(*r_); }
    constexpr sentinel_t<R> end() const { return ranges::end(*r_); }

    constexpr bool empty() const
      requires requires { ranges::empty(*r_); }
    { return ranges::empty(*r_); }

    constexpr auto size() const requires sized_range<R>
    { return ranges::size(*r_); }

    constexpr auto data() const requires contiguous_range<R>
    { return ranges::data(*r_); }

    friend constexpr iterator_t<R> begin(ref_view r)
    { return r.begin(); }

    friend constexpr sentinel_t<R> end(ref_view r)
    { return r.end(); }
  };
  template<class R>
    ref_view(R&) -> ref_view<R>;
}
template<not-same-as<ref_view> T> requires see below constexpr ref_view(T&& t);
Remarks: Let FUN denote the exposition-only functions
void FUN(R&);
void FUN(R&&) = delete;
The expression in the requires-clause is equivalent to
convertible_to<T, R&> && requires { FUN(declval<T>()); }
Effects: Initializes r_­ with addressof(static_­cast<R&>(std​::​forward<T>(t))).

24.7.4 Filter view [range.filter]

24.7.4.1 Overview [range.filter.overview]

filter_­view presents a view of an underlying sequence without the elements that fail to satisfy a predicate.
Example
:
vector<int> is{ 0, 1, 2, 3, 4, 5, 6 };
filter_view evens{is, [](int i) { return 0 == i % 2; }};
for (int i : evens)
  cout << i << ' '; // prints: 0 2 4 6
— end example
 ]

24.7.4.2 Class template filter_­view [range.filter.view]

namespace std::ranges {
  template<input_range V, indirect_unary_predicate<iterator_t<V>> Pred>
    requires view<V> && is_object_v<Pred>
  class filter_view : public view_interface<filter_view<V, Pred>> {
  private:
    V base_ = V();                // exposition only
    semiregular-box<Pred> pred_;  // exposition only

    // [range.filter.iterator], class filter_­view​::​iterator
    class iterator;               // exposition only
    // [range.filter.sentinel], class filter_­view​::​sentinel
    class sentinel;               // exposition only

  public:
    filter_view() = default;
    constexpr filter_view(V base, Pred pred);
    template<input_range R>
      requires viewable_range<R> && constructible_from<V, all_view<R>>
    constexpr filter_view(R&& r, Pred pred);

    constexpr V base() const;

    constexpr iterator begin();
    constexpr auto end() {
      if constexpr (common_range<V>)
        return iterator{*this, ranges::end(base_)};
      else
        return sentinel{*this};
    }
  };

  template<class R, class Pred>
    filter_view(R&&, Pred) -> filter_view<all_view<R>, Pred>;
}
constexpr filter_view(V base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and initializes pred_­ with std​::​move(pred).
template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr filter_view(R&& r, Pred pred);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)) and initializes pred_­ with std​::​​move(pred).
constexpr V base() const;
Effects: Equivalent to: return base_­;
constexpr iterator begin();
Expects: pred_­.has_­value().
Returns: {*this, ranges​::​find_­if(base_­, ref(*pred_­))}.
Remarks: In order to provide the amortized constant time complexity required by the range concept, this function caches the result within the filter_­view for use on subsequent calls.

24.7.4.3 Class filter_­view​::​iterator [range.filter.iterator]

namespace std::ranges {
  template<class V, class Pred>
  class filter_view<V, Pred>::iterator {
  private:
    iterator_t<V> current_ = iterator_t<V>();   // exposition only
    filter_view* parent_ = nullptr;             // exposition only
  public:
    using iterator_concept  = see below;
    using iterator_category = see below;
    using value_type        = range_value_t<V>;
    using difference_type   = range_difference_t<V>;

    iterator() = default;
    constexpr iterator(filter_view& parent, iterator_t<V> current);

    constexpr iterator_t<V> base() const;
    constexpr range_reference_t<V> operator*() const;
    constexpr iterator_t<V> operator->() const
      requires has-arrow<iterator_t<V>>;

    constexpr iterator& operator++();
    constexpr void operator++(int);
    constexpr iterator operator++(int) requires forward_range<V>;

    constexpr iterator& operator--() requires bidirectional_range<V>;
    constexpr iterator operator--(int) requires bidirectional_range<V>;

    friend constexpr bool operator==(const iterator& x, const iterator& y)
      requires equality_comparable<iterator_t<V>>;

    friend constexpr range_rvalue_reference_t<V> iter_move(const iterator& i)
      noexcept(noexcept(ranges::iter_move(i.current_)));
    friend constexpr void iter_swap(const iterator& x, const iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
      requires indirectly_swappable<iterator_t<V>>;
  };
}
Modification of the element a filter_­view​::​iterator denotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.
iterator​::​iterator_­concept is defined as follows:
  • If V models bidirectional_­range, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if V models forward_­range, then iterator_­concept denotes forward_­iterator_­tag.
  • Otherwise, iterator_­concept denotes input_­iterator_­tag.
iterator​::​iterator_­category is defined as follows:
  • Let C denote the type iterator_­traits<iterator_­t<V>>​::​iterator_­category.
  • If C models derived_­from<bidirectional_­iterator_­tag>, then iterator_­category denotes bidirectional_­iterator_­tag.
  • Otherwise, if C models derived_­from<forward_­iterator_­tag>, then iterator_­category denotes forward_­iterator_­tag.
  • Otherwise, iterator_­category denotes input_­iterator_­tag.
constexpr iterator(filter_view& parent, iterator_t<V> current);
Effects: Initializes current_­ with current and parent_­ with addressof(parent).
constexpr iterator_t<V> base() const;
Effects: Equivalent to: return current_­;
constexpr range_reference_t<V> operator*() const;
Effects: Equivalent to: return *current_­;
constexpr iterator_t<V> operator->() const requires has-arrow<iterator_t<V>>;
Effects: Equivalent to: return current_­;
constexpr iterator& operator++();
Effects: Equivalent to:
current_ = ranges::find_if(++current_, ranges::end(parent_->base_), ref(*parent_->pred_));
return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
constexpr iterator operator++(int) requires forward_range<V>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires bidirectional_range<V>;
Effects: Equivalent to:
do
  --current_;
while (!invoke(*parent_->pred_, *current_));
return *this;
constexpr iterator operator--(int) requires bidirectional_range<V>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_comparable<iterator_t<V>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr range_rvalue_reference_t<V> iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(i.current_)));
Effects: Equivalent to: return ranges​::​iter_­move(i.current_­);
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.current_, y.current_))) requires indirectly_swappable<iterator_t<V>>;
Effects: Equivalent to ranges​::​iter_­swap(x.current_­, y.current_­).

24.7.4.4 Class filter_­view​::​sentinel [range.filter.sentinel]

namespace std::ranges {
  template<class V, class Pred>
  class filter_view<V, Pred>::sentinel {
  private:
    sentinel_t<V> end_ = sentinel_t<V>();       // exposition only
  public:
    sentinel() = default;
    constexpr explicit sentinel(filter_view& parent);

    constexpr sentinel_t<V> base() const;

    friend constexpr bool operator==(const iterator& x, const sentinel& y);
  };
}
constexpr explicit sentinel(filter_view& parent);
Effects: Initializes end_­ with ranges​::​end(parent.base_­).
constexpr sentinel_t<V> base() const;
Effects: Equivalent to: return end_­;
friend constexpr bool operator==(const iterator& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ == y.end_­;

24.7.4.5 views​::​filter [range.filter.adaptor]

The name views​::​filter denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and P, the expression views​::​filter(E, P) is expression-equivalent to filter_­view{E, P}.

24.7.5 Transform view [range.transform]

24.7.5.1 Overview [range.transform.overview]

transform_­view presents a view of an underlying sequence after applying a transformation function to each element.
Example
:
vector<int> is{ 0, 1, 2, 3, 4 };
transform_view squares{is, [](int i) { return i * i; }};
for (int i : squares)
  cout << i << ' '; // prints: 0 1 4 9 16
— end example
 ]

24.7.5.2 Class template transform_­view [range.transform.view]

namespace std::ranges {
  template<input_range V, copy_constructible F>
    requires view<V> && is_object_v<F> &&
             regular_invocable<F&, range_reference_t<V>>
  class transform_view : public view_interface<transform_view<V, F>> {
  private:
    // [range.transform.iterator], class template transform_­view​::​iterator
    template<bool> struct iterator;             // exposition only
    // [range.transform.sentinel], class template transform_­view​::​sentinel
    template<bool> struct sentinel;             // exposition only

    V base_ = V();                              // exposition only
    semiregular-box<F> fun_;                    // exposition only

  public:
    transform_view() = default;
    constexpr transform_view(V base, F fun);
    template<input_range R>
      requires viewable_range<R> && constructible_from<V, all_view<R>>
    constexpr transform_view(R&& r, F fun);

    constexpr V base() const;

    constexpr iterator<false> begin();
    constexpr iterator<true> begin() const
      requires range<const V> &&
               regular_invocable<const F&, range_reference_t<const V>>;

    constexpr sentinel<false> end();
    constexpr iterator<false> end() requires common_range<V>;
    constexpr sentinel<true> end() const
      requires range<const V> &&
               regular_invocable<const F&, range_reference_t<const V>>;
    constexpr iterator<true> end() const
      requires common_range<const V> &&
               regular_invocable<const F&, range_reference_t<const V>>;

    constexpr auto size() requires sized_range<V> { return ranges::size(base_); }
    constexpr auto size() const requires sized_range<const V>
    { return ranges::size(base_); }
  };

  template<class R, class F>
    transform_view(R&&, F) -> transform_view<all_view<R>, F>;
}
constexpr transform_view(V base, F fun);
Effects: Initializes base_­ with std​::​move(base) and fun_­ with std​::​move(fun).
template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr transform_view(R&& r, F fun);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)) and fun_­ with std​::​move(fun).
constexpr V base() const;
Effects: Equivalent to: return base_­;
constexpr iterator<false> begin();
Effects: Equivalent to:
return iterator<false>{*this, ranges::begin(base_)};
constexpr iterator<true> begin() const requires range<const V> && regular_invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to:
return iterator<true>{*this, ranges::begin(base_)};
constexpr sentinel<false> end();
Effects: Equivalent to:
return sentinel<false>{ranges::end(base_)};
constexpr iterator<false> end() requires common_range<V>;
Effects: Equivalent to:
return iterator<false>{*this, ranges::end(base_)};
constexpr sentinel<true> end() const requires range<const V> && regular_invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to:
return sentinel<true>{ranges::end(base_)};
constexpr iterator<true> end() const requires common_range<const V> && regular_invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to:
return iterator<true>{*this, ranges::end(base_)};

24.7.5.3 Class template transform_­view​::​iterator [range.transform.iterator]

namespace std::ranges {
  template<class V, class F>
  template<bool Const>
  class transform_view<V, F>::iterator {
  private:
    using Parent =                              // exposition only
      conditional_t<Const, const transform_view, transform_view>;
    using Base   =                              // exposition only
      conditional_t<Const, const V, V>;
    iterator_t<Base> current_ =                 // exposition only
      iterator_t<Base>();
    Parent* parent_ = nullptr;                  // exposition only
  public:
    using iterator_concept  = see below;
    using iterator_category = see below;
    using value_type        =
      remove_cvref_t<invoke_result_t<F&, range_reference_t<Base>>>;
    using difference_type   = range_difference_t<Base>;

    iterator() = default;
    constexpr iterator(Parent& parent, iterator_t<Base> current);
    constexpr iterator(iterator<!Const> i)
      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;

    constexpr iterator_t<Base> base() const;
    constexpr decltype(auto) operator*() const
    { return invoke(*parent_->fun_, *current_); }

    constexpr iterator& operator++();
    constexpr void operator++(int);
    constexpr iterator operator++(int) requires forward_range<Base>;

    constexpr iterator& operator--() requires bidirectional_range<Base>;
    constexpr iterator operator--(int) requires bidirectional_range<Base>;

    constexpr iterator& operator+=(difference_type n)
      requires random_access_range<Base>;
    constexpr iterator& operator-=(difference_type n)
      requires random_access_range<Base>;
    constexpr decltype(auto) operator[](difference_type n) const
      requires random_access_range<Base>
    { return invoke(*parent_->fun_, current_[n]); }

    friend constexpr bool operator==(const iterator& x, const iterator& y)
      requires equality_comparable<iterator_t<Base>>;

    friend constexpr bool operator<(const iterator& x, const iterator& y)
      requires random_access_range<Base>;
    friend constexpr bool operator>(const iterator& x, const iterator& y)
      requires random_access_range<Base>;
    friend constexpr bool operator<=(const iterator& x, const iterator& y)
      requires random_access_range<Base>;
    friend constexpr bool operator>=(const iterator& x, const iterator& y)
      requires random_access_range<Base>;
    friend constexpr compare_three_way_result_t<iterator_t<Base>>
      operator<=>(const iterator& x, const iterator& y)
        requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>;

    friend constexpr iterator operator+(iterator i, difference_type n)
      requires random_access_range<Base>;
    friend constexpr iterator operator+(difference_type n, iterator i)
      requires random_access_range<Base>;

    friend constexpr iterator operator-(iterator i, difference_type n)
      requires random_access_range<Base>;
    friend constexpr difference_type operator-(const iterator& x, const iterator& y)
      requires random_access_range<Base>;

    friend constexpr decltype(auto) iter_move(const iterator& i)
      noexcept(noexcept(invoke(*i.parent_->fun_, *i.current_)))
    {
      if constexpr (is_lvalue_reference_v<decltype(*i)>)
        return std::move(*i);
      else
        return *i;
    }

    friend constexpr void iter_swap(const iterator& x, const iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
      requires indirectly_swappable<iterator_t<Base>>;
  };
}
iterator​::​iterator_­concept is defined as follows:
  • If V models random_­access_­range, then iterator_­concept denotes random_­access_­iterator_­tag.
  • Otherwise, if V models bidirectional_­range, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if V models forward_­range, then iterator_­concept denotes forward_­iterator_­tag.
  • Otherwise, iterator_­concept denotes input_­iterator_­tag.
Let C denote the type iterator_­traits<iterator_­t<Base>>​::​iterator_­category.
If C models derived_­from<contiguous_­iterator_­tag>, then iterator_­category denotes random_­access_­iterator_­tag; otherwise, iterator_­category denotes C.
constexpr iterator(Parent& parent, iterator_t<Base> current);
Effects: Initializes current_­ with current and parent_­ with addressof(parent).
constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
Effects: Initializes current_­ with std​::​move(i.current_­) and parent_­ with i.parent_­.
constexpr iterator_t<Base> base() const;
Effects: Equivalent to: return current_­;
constexpr iterator& operator++();
Effects: Equivalent to:
++current_;
return *this;
constexpr void operator++(int);
Effects: Equivalent to ++current_­.
constexpr iterator operator++(int) requires forward_range<Base>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires bidirectional_range<Base>;
Effects: Equivalent to:
--current_;
return *this;
constexpr iterator operator--(int) requires bidirectional_range<Base>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
constexpr iterator& operator+=(difference_type n) requires random_access_range<Base>;
Effects: Equivalent to:
current_ += n;
return *this;
constexpr iterator& operator-=(difference_type n) requires random_access_range<Base>;
Effects: Equivalent to:
current_ -= n;
return *this;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_comparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_access_range<Base>;
Effects: Equivalent to: return x.current_­ < y.current_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_access_range<Base>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_access_range<Base>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_access_range<Base>;
Effects: Equivalent to: return !(x < y);
friend constexpr compare_three_way_result_t<iterator_t<Base>> operator<=>(const iterator& x, const iterator& y) requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ <=> y.current_­;
friend constexpr iterator operator+(iterator i, difference_type n) requires random_access_range<Base>; friend constexpr iterator operator+(difference_type n, iterator i) requires random_access_range<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ + n};
friend constexpr iterator operator-(iterator i, difference_type n) requires random_access_range<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ - n};
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires random_access_range<Base>;
Effects: Equivalent to: return x.current_­ - y.current_­;
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.current_, y.current_))) requires indirectly_swappable<iterator_t<Base>>;
Effects: Equivalent to ranges​::​iter_­swap(x.current_­, y.current_­).

24.7.5.4 Class template transform_­view​::​sentinel [range.transform.sentinel]

namespace std::ranges {
  template<class V, class F>
  template<bool Const>
  class transform_view<V, F>::sentinel {
  private:
    using Parent =                                      // exposition only
      conditional_t<Const, const transform_view, transform_view>;
    using Base = conditional_t<Const, const V, V>;      // exposition only
    sentinel_t<Base> end_ = sentinel_t<Base>();         // exposition only
  public:
    sentinel() = default;
    constexpr explicit sentinel(sentinel_t<Base> end);
    constexpr sentinel(sentinel<!Const> i)
      requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

    constexpr sentinel_t<Base> base() const;

    friend constexpr bool operator==(const iterator<Const>& x, const sentinel& y);

    friend constexpr range_difference_t<Base>
      operator-(const iterator<Const>& x, const sentinel& y)
        requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
    friend constexpr range_difference_t<Base>
      operator-(const sentinel& y, const iterator<Const>& x)
        requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
  };
}
constexpr explicit sentinel(sentinel_t<Base> end);
Effects: Initializes end_­ with end.
constexpr sentinel(sentinel<!Const> i) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(i.end_­).
constexpr sentinel_t<Base> base() const;
Effects: Equivalent to: return end_­;
friend constexpr bool operator==(const iterator<Const>& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ == y.end_­;
friend constexpr range_difference_t<Base> operator-(const iterator<Const>& x, const sentinel& y) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ - y.end_­;
friend constexpr range_difference_t<Base> operator-(const sentinel& y, const iterator<Const>& x) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.end_­ - y.current_­;

24.7.5.5 views​::​transform [range.transform.adaptor]

The name views​::​transform denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​transform(E, F) is expression-equivalent to transform_­view{E, F}.

24.7.6 Take view [range.take]

24.7.6.1 Overview [range.take.overview]

take_­view produces a view of the first N elements from another view, or all the elements if the adapted view contains fewer than N.
Example
:
vector<int> is{0,1,2,3,4,5,6,7,8,9};
take_view few{is, 5};
for (int i : few)
  cout << i << ' '; // prints: 0 1 2 3 4
— end example
 ]

24.7.6.2 Class template take_­view [range.take.view]

namespace std::ranges {
  template<view V>
  class take_view : public view_interface<take_view<V>> {
  private:
    V base_ = V();                                      // exposition only
    range_difference_t<V> count_ = 0;                   // exposition only
    // [range.take.sentinel], class template take_­view​::​sentinel
    template<bool> struct sentinel;                     // exposition only
  public:
    take_view() = default;
    constexpr take_view(V base, range_difference_t<V> count);
    template<viewable_range R>
      requires constructible_from<V, all_view<R>>
    constexpr take_view(R&& r, range_difference_t<V> count);

    constexpr V base() const;

    constexpr auto begin() requires (!simple-view<V>) {
      if constexpr (sized_range<V>) {
        if constexpr (random_access_range<V>)
          return ranges::begin(base_);
        else
          return counted_iterator{ranges::begin(base_), size()};
      } else
        return counted_iterator{ranges::begin(base_), count_};
    }

    constexpr auto begin() const requires range<const V> {
      if constexpr (sized_range<const V>) {
        if constexpr (random_access_range<const V>)
          return ranges::begin(base_);
        else
          return counted_iterator{ranges::begin(base_), size()};
      } else
        return counted_iterator{ranges::begin(base_), count_};
    }

    constexpr auto end() requires (!simple-view<V>) {
      if constexpr (sized_range<V>) {
        if constexpr (random_access_range<V>)
          return ranges::begin(base_) + size();
        else
          return default_sentinel;
      } else
        return sentinel<false>{ranges::end(base_)};
    }

    constexpr auto end() const requires range<const V> {
      if constexpr (sized_range<const V>) {
        if constexpr (random_access_range<const V>)
          return ranges::begin(base_) + size();
        else
          return default_sentinel;
      } else
        return sentinel<true>{ranges::end(base_)};
    }

    constexpr auto size() requires sized_range<V> {
      auto n = ranges::size(base_);
      return ranges::min(n, static_cast<decltype(n)>(count_));
    }

    constexpr auto size() const requires sized_range<const V> {
      auto n = ranges::size(base_);
      return ranges::min(n, static_cast<decltype(n)>(count_));
    }
  };

  template<range R>
    take_view(R&&, range_difference_t<R>)
      -> take_view<all_view<R>>;
}
constexpr take_view(V base, range_difference_t<V> count);
Effects: Initializes base_­ with std​::​move(base) and count_­ with count.
template<viewable_range R> requires constructible_from<V, all_view<R>> constexpr take_view(R&& r, range_difference_t<V> count);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)) and count_­ with count.
constexpr V base() const;
Effects: Equivalent to: return base_­;

24.7.6.3 Class template take_­view​::​sentinel [range.take.sentinel]

namespace std::ranges {
  template<class V>
  template<bool Const>
  class take_view<V>::sentinel {
  private:
    using Base = conditional_t<Const, const V, V>;      // exposition only
    using CI = counted_iterator<iterator_t<Base>>;      // exposition only
    sentinel_t<Base> end_ = sentinel_t<Base>();         // exposition only
  public:
    sentinel() = default;
    constexpr explicit sentinel(sentinel_t<Base> end);
    constexpr sentinel(sentinel<!Const> s)
      requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

    constexpr sentinel_t<Base> base() const;

    friend constexpr bool operator==(const CI& y, const sentinel& x);
  };
}
constexpr explicit sentinel(sentinel_t<Base> end);
Effects: Initializes end_­ with end.
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(s.end_­).
constexpr sentinel_t<Base> base() const;
Effects: Equivalent to: return end_­;
friend constexpr bool operator==(const CI& y, const sentinel& x);
Effects: Equivalent to: return y.count() == 0 || y.base() == x.end_­;

24.7.6.4 views​::​take [range.take.adaptor]

The name views​::​take denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​take(E, F) is expression-equivalent to take_­view{E, F}.

24.7.7 Take while view [range.take.while]

24.7.7.1 Overview [range.take.while.overview]

Given a unary predicate pred and a view r, take_­while_­view produces a view of the range [begin(r), ranges​::​find_­if_­not(r, pred)).
Example
:
auto input = istringstream{"0 1 2 3 4 5 6 7 8 9"};
auto small = [](const auto x) noexcept { return x < 5; };
auto small_ints = istream_view<int>(input) | views::take_while(small);
for (const auto i : small_ints) {
  cout << i << ' ';                             // prints 0 1 2 3 4
}
auto i = 0;
input >> i;
cout << i;                                      // prints 6
— end example
 ]

24.7.7.2 Class template take_­while_­view [range.take.while.view]

namespace std::ranges {
  template<view R, class Pred>
  requires input_range<R> && is_object_v<Pred> &&
    indirect_unary_predicate<const Pred, iterator_t<R>>
  class take_while_view : public view_interface<take_while_view<R, Pred>> {
    template<bool> class sentinel;                      // exposition only

    R base_;                                            // exposition only
    semiregular-box<Pred> pred_;                        // exposition only

  public:
    take_while_view() = default;
    constexpr take_while_view(R base, Pred pred);

    constexpr R base() const;
    constexpr const Pred& pred() const;

    constexpr auto begin() requires (!simple-view<R>)
    { return ranges::begin(base_); }

    constexpr auto begin() const requires range<const R>
    { return ranges::begin(base_); }

    constexpr auto end() requires (!simple-view<R>)
    { return sentinel<false>(ranges::end(base_), addressof(*pred_)); }

    constexpr auto end() const requires range<const R>
    { return sentinel<true>(ranges::end(base_), addressof(*pred_)); }
  };

  template<class R, class Pred>
    take_while_view(R&&, Pred) -> take_while_view<all_view<R>, Pred>;
}
constexpr take_while_view(R base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and pred_­ with std​::​move(pred).
constexpr R base() const;
Effects: Equivalent to: return base_­;
constexpr const Pred& pred() const;
Effects: Equivalent to: return *pred_­;

24.7.7.3 Class template take_­while_­view​::​sentinel [range.take.while.sentinel]

namespace std::ranges {
  template<class V>
  template<bool Const>
  class take_while_view<V>::sentinel {                  // exposition only
    using base_t = conditional_t<Const, const V, V>;    // exposition only

    sentinel_t<base_t> end_ = sentinel_t<base_t>();     // exposition only
    const Pred* pred_{};                                // exposition only
  public:
    sentinel() = default;
    constexpr explicit sentinel(sentinel_t<base_t> end, const Pred* pred);
    constexpr sentinel(sentinel<!Const> s)
      requires Const && convertible_to<sentinel_t<V>, sentinel_t<base_t>>;

    constexpr sentinel_t<base_t> base() const { return end_; }

    friend constexpr bool operator==(const iterator_t<base_t>& x, const sentinel& y);
  };
}
constexpr explicit sentinel(sentinel_t<base_t> end, const Pred* pred);
Effects: Initializes end_­ with end and pred_­ with pred.
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_to<sentinel_t<R>, sentinel_t<base_t>>;
Effects: Initializes end_­ with s.end_­ and pred_­ with s.pred_­.
friend constexpr bool operator==(const iterator_t<base_t>& x, const sentinel& y);
Effects: Equivalent to: return y.end_­ == x || !invoke(*y.pred_­, *x);

24.7.7.4 views​::​take_­while [range.take.while.adaptor]

The name views​::​take_­while denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​take_­while(E, F) is expression-equivalent to take_­while_­view{E, F}.

24.7.8 Drop view [range.drop]

24.7.8.1 Overview [range.drop.overview]

drop_­view produces a view excluding the first N elements from another view, or an empty range if the adapted view contains fewer than N elements.
Example
:
auto ints = views::iota(0) | views::take(10);
auto latter_half = drop_view{ints, 5};
for (auto i : latter_half) {
  cout << i << ' ';                             // prints 5 6 7 8 9
}
— end example
 ]

24.7.8.2 Class template drop_­view [range.drop.view]

namespace std::ranges {
  template<view R>
  class drop_view : public view_interface<drop_view<R>> {
  public:
    drop_view() = default;
    constexpr drop_view(R base, range_difference_t<R> count);

    constexpr R base() const;

    constexpr auto begin()
      requires (!(simple-view<R> && random_access_range<R>));
    constexpr auto begin() const
      requires random_access_range<const R>;

    constexpr auto end()
      requires (!simple-view<R>)
    { return ranges::end(base_); }

    constexpr auto end() const
      requires range<const R>
    { return ranges::end(base_); }

    constexpr auto size()
      requires sized_range<R>
    {
      const auto s = ranges::size(base_);
      const auto c = static_cast<decltype(s)>(count_);
      return s < c ? 0 : s - c;
    }

    constexpr auto size() const
      requires sized_range<const R>
    {
      const auto s = ranges::size(base_);
      const auto c = static_cast<decltype(s)>(count_);
      return s < c ? 0 : s - c;
    }
  private:
    R base_;                                    // exposition only
    range_difference_t<R> count_;               // exposition only
  };

  template<class R>
    drop_view(R&&, range_difference_t<R>) -> drop_view<all_view<R>>;
}
constexpr drop_view(R base, range_difference_t<R> count);
Expects: count >= 0 is true.
Effects: Initializes base_­ with std​::​move(base) and count_­ with count.
constexpr R base() const;
Effects: Equivalent to: return base_­;
constexpr auto begin() requires (!(simple-view<R> && random_access_range<R>)); constexpr auto begin() const requires random_access_range<const R>;
Returns: ranges​::​next(ranges​::​begin(base_­), count_­, ranges​::​end(base_­)).
Remarks: In order to provide the amortized constant-time complexity required by the range concept, the first overload caches the result within the drop_­view for use on subsequent calls.
Note
:
Without this, applying a reverse_­view over a drop_­view would have quadratic iteration complexity.
— end note
 ]

24.7.8.3 views​::​drop [range.drop.adaptor]

The name views​::​drop denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​drop(E, F) is expression-equivalent to drop_­view{E, F}.

24.7.9 Drop while view [range.drop.while]

24.7.9.1 Overview [range.drop.while.overview]

Given a unary predicate pred and a view r, drop_­while_­view produces a view of the range [ranges​::​find_­if_­not(r, pred), ranges​::​end(r)).
Example
:
constexpr auto source = "  \t   \t   \t   hello there";
auto is_invisible = [](const auto x) { return x == ' ' || x == '\t'; };
auto skip_ws = drop_while_view{source, is_invisible};
for (auto c : skip_ws) {
  cout << c;                                    // prints hello there with no leading space
}
— end example
 ]

24.7.9.2 Class template drop_­while_­view [range.drop.while.view]

namespace std::ranges {
  template<view R, class Pred>
  requires input_range<R> && is_object_v<Pred> &&
    indirect_unary_predicate<const Pred, iterator_t<R>>
  class drop_while_view : public view_interface<drop_while_view<R, Pred>> {
  public:
    drop_while_view() = default;
    constexpr drop_while_view(R base, Pred pred);

    constexpr R base() const;
    constexpr const Pred& pred() const;

    constexpr auto begin();

    constexpr auto end()
    { return ranges::end(base_); }

  private:
    R base_;                                            // exposition only
    semiregular-box<Pred> pred_;                        // exposition only
  };

  template<class R, class Pred>
    drop_while_view(R&&, Pred) -> drop_while_view<all_view<R>, Pred>;
}
constexpr drop_while_view(R base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and pred_­ with std​::​move(pred).
constexpr R base() const;
Effects: Equivalent to: return base_­;
constexpr const Pred& pred() const;
Effects: Equivalent to: return *pred_­;
constexpr auto begin();
Returns: ranges​::​find_­if_­not(base_­, cref(*pred_­)).
Remarks: In order to provide the amortized constant-time complexity required by the range concept, the first call caches the result within the drop_­while_­view for use on subsequent calls.
Note
:
Without this, applying a reverse_­view over a drop_­while_­view would have quadratic iteration complexity.
— end note
 ]

24.7.9.3 views​::​drop_­while [range.drop.while.adaptor]

The name views​::​drop_­while denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​drop_­while(E, F) is expression-equivalent to drop_­while_­view{E, F}.

24.7.10 Join view [range.join]

24.7.10.1 Overview [range.join.overview]

join_­view flattens a view of ranges into a view.
Example
:
vector<string> ss{"hello", " ", "world", "!"};
join_view greeting{ss};
for (char ch : greeting)
  cout << ch;                                   // prints: hello world!
— end example
 ]

24.7.10.2 Class template join_­view [range.join.view]

namespace std::ranges {
  template<input_range V>
    requires view<V> && input_range<range_reference_t<V>> &&
             (is_reference_v<range_reference_t<V>> ||
              view<range_value_t<V>>)
  class join_view : public view_interface<join_view<V>> {
  private:
    using InnerRng =                    // exposition only
      range_reference_t<V>;
    // [range.join.iterator], class template join_­view​::​iterator
    template<bool Const>
      struct iterator;                  // exposition only
    // [range.join.sentinel], class template join_­view​::​sentinel
    template<bool Const>
      struct sentinel;                  // exposition only

    V base_ = V();                      // exposition only
    all_view<InnerRng> inner_ =         // exposition only, present only when !is_­reference_­v<InnerRng>
      all_view<InnerRng>();
  public:
    join_view() = default;
    constexpr explicit join_view(V base);

    template<input_range R>
      requires viewable_range<R> && constructible_from<V, all_view<R>>
    constexpr explicit join_view(R&& r);

    constexpr auto begin() {
      return iterator<simple-view<V>>{*this, ranges::begin(base_)};
    }

    constexpr auto begin() const
    requires input_range<const V> &&
             is_reference_v<range_reference_t<const V>> {
      return iterator<true>{*this, ranges::begin(base_)};
    }

    constexpr auto end() {
      if constexpr (forward_range<V> &&
                    is_reference_v<InnerRng> && forward_range<InnerRng> &&
                    common_range<V> && common_range<InnerRng>)
        return iterator<simple-view<V>>{*this, ranges::end(base_)};
      else
        return sentinel<simple-view<V>>{*this};
    }

    constexpr auto end() const
    requires input_range<const V> &&
             is_reference_v<range_reference_t<const V>> {
      if constexpr (forward_range<const V> &&
                    is_reference_v<range_reference_t<const V>> &&
                    forward_range<range_reference_t<const V>> &&
                    common_range<const V> &&
                    common_range<range_reference_t<const V>>)
        return iterator<true>{*this, ranges::end(base_)};
      else
        return sentinel<true>{*this};
    }
  };

  template<class R>
    explicit join_view(R&&) -> join_view<all_view<R>>;
}
constexpr explicit join_view(V base);
Effects: Initializes base_­ with std​::​move(base).
template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr explicit join_view(R&& r);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)).

24.7.10.3 Class template join_­view​::​iterator [range.join.iterator]

namespace std::ranges {
template<class V>
  template<bool Const>
  struct join_view<V>::iterator {
  private:
    using Parent =                                              // exposition only
      conditional_t<Const, const join_view, join_view>;
    using Base   = conditional_t<Const, const V, V>;            // exposition only

    static constexpr bool ref_is_glvalue =                      // exposition only
      is_reference_v<range_reference_t<Base>>;

    iterator_t<Base> outer_ = iterator_t<Base>();               // exposition only
    iterator_t<range_reference_t<Base>> inner_ =                // exposition only
      iterator_t<range_reference_t<Base>>();
    Parent* parent_ = nullptr;                                  // exposition only

    constexpr void satisfy();                                   // exposition only
  public:
    using iterator_concept  = see below;
    using iterator_category = see below;
    using value_type        = range_value_t<range_reference_t<Base>>;
    using difference_type   = see below;

    iterator() = default;
    constexpr iterator(Parent& parent, iterator_t<V> outer);
    constexpr iterator(iterator<!Const> i)
      requires Const &&
               convertible_to<iterator_t<V>, iterator_t<Base>> &&
               convertible_to<iterator_t<InnerRng>,
                              iterator_t<range_reference_t<Base>>>;

    constexpr decltype(auto) operator*() const { return *inner_; }

    constexpr iterator_t<Base> operator->() const
      requires has-arrow<iterator_t<Base>>;

    constexpr iterator& operator++();
    constexpr void operator++(int);
    constexpr iterator operator++(int)
      requires ref_is_glvalue && forward_range<Base> &&
               forward_range<range_reference_t<Base>>;

    constexpr iterator& operator--()
      requires ref_is_glvalue && bidirectional_range<Base> &&
               bidirectional_range<range_reference_t<Base>>;

    constexpr iterator operator--(int)
      requires ref_is_glvalue && bidirectional_range<Base> &&
               bidirectional_range<range_reference_t<Base>>;

    friend constexpr bool operator==(const iterator& x, const iterator& y)
      requires ref_is_glvalue && equality_comparable<iterator_t<Base>> &&
               equality_comparable<iterator_t<range_reference_t<Base>>>;

    friend constexpr decltype(auto) iter_move(const iterator& i)
    noexcept(noexcept(ranges::iter_move(i.inner_))) {
      return ranges::iter_move(i.inner_);
    }

    friend constexpr void iter_swap(const iterator& x, const iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_)));
  };
}
iterator​::​iterator_­concept is defined as follows:
  • If ref_­is_­glvalue is true,
    • If Base and range_­reference_­t<Base> each model bidirectional_­range, then iterator_­concept denotes bidirectional_­iterator_­tag.
    • Otherwise, if Base and range_­reference_­t<Base> each model forward_­range, then iterator_­concept denotes forward_­iterator_­tag.
  • Otherwise, iterator_­concept denotes input_­iterator_­tag.
iterator​::​iterator_­category is defined as follows:
  • Let OUTERC denote iterator_­traits<iterator_­t<Base>>​::​iterator_­category, and let INNERC denote iterator_­traits<iterator_­t<range_­reference_­t<Base>>>​::​iterator_­category.
  • If ref_­is_­glvalue is true,
    • If OUTERC and INNERC each model derived_­from<bidirectional_­iterator_­tag>, iterator_­category denotes bidirectional_­iterator_­tag.
    • Otherwise, if OUTERC and INNERC each model derived_­from<forward_­iterator_­tag>, iterator_­category denotes forward_­iterator_­tag.
  • Otherwise, iterator_­category denotes input_­iterator_­tag.
iterator​::​difference_­type denotes the type:
common_type_t<
  range_difference_t<Base>,
  range_difference_t<range_reference_t<Base>>>
join_­view iterators use the satisfy function to skip over empty inner ranges.
constexpr void satisfy(); // exposition only
Effects: Equivalent to:
auto update_inner = [this](range_reference_t<Base> x) -> decltype(auto) {
  if constexpr (ref_is_glvalue) // x is a reference
    return (x);                 // (x) is an lvalue
  else
    return (parent_->inner_ = views::all(x));
};

for (; outer_ != ranges::end(parent_->base_); ++outer_) {
  auto& inner = update_inner(*outer_);
  inner_ = ranges::begin(inner);
  if (inner_ != ranges::end(inner))
    return;
}
if constexpr (ref_is_glvalue)
  inner_ = iterator_t<range_reference_t<Base>>();
constexpr iterator(Parent& parent, iterator_t<V> outer)
Effects: Initializes outer_­ with outer and parent_­ with addressof(parent); then calls satisfy().
constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, iterator_t<Base>> && convertible_to<iterator_t<InnerRng>, iterator_t<range_reference_t<Base>>>;
Effects: Initializes outer_­ with std​::​move(i.outer_­), inner_­ with std​::​move(i.inner_­), and parent_­ with i.parent_­.
constexpr iterator_t<Base> operator->() const requires has-arrow<iterator_t<Base>>;
Effects: Equivalent to return inner_­;
constexpr iterator& operator++();
Let inner-range be:
  • If ref_­is_­glvalue is true, *outer_­.
  • Otherwise, parent_­->inner_­.
Effects: Equivalent to:
auto&& inner_rng = inner-range;
if (++inner_ == ranges::end(inner_rng)) {
  ++outer_;
  satisfy();
}
return *this;
constexpr void operator++(int);
Effects: Equivalent to: ++*this.
constexpr iterator operator++(int) requires ref_is_glvalue && forward_range<Base> && forward_range<range_reference_t<Base>>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>;
Effects: Equivalent to:
if (outer_ == ranges::end(parent_->base_))
  inner_ = ranges::end(*--outer_);
while (inner_ == ranges::begin(*outer_))
  inner_ = ranges::end(*--outer_);
--inner_;
return *this;
constexpr iterator operator--(int) requires ref_is_glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref_is_glvalue && equality_comparable<iterator_t<Base>> && equality_comparable<iterator_t<range_reference_t<Base>>>;
Effects: Equivalent to: return x.outer_­ == y.outer_­ && x.inner_­ == y.inner_­;
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_)));
Effects: Equivalent to: return ranges​::​iter_­swap(x.inner_­, y.inner_­);

24.7.10.4 Class template join_­view​::​sentinel [range.join.sentinel]

namespace std::ranges {
  template<class V>
  template<bool Const>
  struct join_view<V>::sentinel {
  private:
    using Parent =                                      // exposition only
      conditional_t<Const, const join_view, join_view>;
    using Base   = conditional_t<Const, const V, V>;    // exposition only
    sentinel_t<Base> end_ = sentinel_t<Base>();         // exposition only
  public:
    sentinel() = default;

    constexpr explicit sentinel(Parent& parent);
    constexpr sentinel(sentinel<!Const> s)
      requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

    friend constexpr bool operator==(const iterator<Const>& x, const sentinel& y);
  };
}
constexpr explicit sentinel(Parent& parent);
Effects: Initializes end_­ with ranges​::​end(parent.base_­).
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(s.end_­).
friend constexpr bool operator==(const iterator<Const>& x, const sentinel& y);
Effects: Equivalent to: return x.outer_­ == y.end_­;

24.7.10.5 views​::​join [range.join.adaptor]

The name views​::​join denotes a range adaptor object ([range.adaptor.object]).
For some subexpression E, the expression views​::​join(E) is expression-equivalent to join_­view{E}.

24.7.11 Split view [range.split]

24.7.11.1 Overview [range.split.overview]

split_­view takes a view and a delimiter, and splits the view into subranges on the delimiter.
The delimiter can be a single element or a view of elements.
Example
:
string str{"the quick brown fox"};
split_view sentence{str, ' '};
for (auto word : sentence) {
  for (char ch : word)
    cout << ch;
  cout << '*';
}
// The above prints: the*quick*brown*fox*
— end example
 ]

24.7.11.2 Class template split_­view [range.split.view]

namespace std::ranges {
  template<auto> struct require-constant;       // exposition only

  template<class R>
  concept tiny-range =                          // exposition only
    sized_range<R> &&
    requires { typename require-constant<remove_reference_t<R>::size()>; } &&
    (remove_reference_t<R>::size() <= 1);

  template<input_range V, forward_range Pattern>
    requires view<V> && view<Pattern> &&
             indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (forward_range<V> || tiny-range<Pattern>)
  class split_view : public view_interface<split_view<V, Pattern>> {
  private:
    V base_ = V();                              // exposition only
    Pattern pattern_ = Pattern();               // exposition only
    iterator_t<V> current_ = iterator_t<V>();   // exposition only, present only if !forward_­range<V>
    // [range.split.outer], class template split_­view​::​outer_­iterator
    template<bool> struct outer_iterator;       // exposition only
    // [range.split.inner], class template split_­view​::​inner_­iterator
    template<bool> struct inner_iterator;       // exposition only
  public:
    split_view() = default;
    constexpr split_view(V base, Pattern pattern);

    template<input_range R, forward_range P>
      requires constructible_from<V, all_view<R>> &&
               constructible_from<Pattern, all_view<P>>
    constexpr split_view(R&& r, P&& p);

    template<input_range R>
      requires constructible_from<V, all_view<R>> &&
               constructible_from<Pattern, single_view<range_value_t<R>>>
    constexpr split_view(R&& r, range_value_t<R> e);

    constexpr auto begin() {
      if constexpr (forward_range<V>)
        return outer_iterator<simple-view<V>>{*this, ranges::begin(base_)};
      else {
        current_ = ranges::begin(base_);
        return outer_iterator<false>{*this};
      }
    }

    constexpr auto begin() const requires forward_range<V> && forward_range<const V> {
      return outer_iterator<true>{*this, ranges::begin(base_)};
    }

    constexpr auto end() requires forward_range<V> && common_range<V> {
      return outer_iterator<simple-view<V>>{*this, ranges::end(base_)};
    }

    constexpr auto end() const {
      if constexpr (forward_range<V> && forward_range<const V> && common_range<const V>)
        return outer_iterator<true>{*this, ranges::end(base_)};
      else
        return default_sentinel;
    }
  };

  template<class R, class P>
    split_view(R&&, P&&) -> split_view<all_view<R>, all_view<P>>;

  template<input_range R>
    split_view(R&&, range_value_t<R>)
      -> split_view<all_view<R>, single_view<range_value_t<R>>>;
}
constexpr split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std​::​move(base), and pattern_­ with std​::​move(pattern).
template<input_range R, forward_range P> requires constructible_from<V, all_view<R>> && constructible_from<Pattern, all_view<P>> constexpr split_view(R&& r, P&& p);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)), and pattern_­ with views​::​all(​std​::​forward<P>(p)).
template<input_range R> requires constructible_from<V, all_view<R>> && constructible_from<Pattern, single_view<range_value_t<R>>> constexpr split_view(R&& r, range_value_t<R> e);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)), and pattern_­ with single_­view{​std​::​move(e)}.

24.7.11.3 Class template split_­view​::​outer_­iterator [range.split.outer]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::outer_iterator {
  private:
    using Parent =                              // exposition only
      conditional_t<Const, const split_view, split_view>;
    using Base   =                              // exposition only
      conditional_t<Const, const V, V>;
    Parent* parent_ = nullptr;                  // exposition only
    iterator_t<Base> current_ =                 // exposition only, present only if V models forward_­range
      iterator_t<Base>();

  public:
    using iterator_concept  =
      conditional_t<forward_range<Base>, forward_iterator_tag, input_iterator_tag>;
    using iterator_category = input_iterator_tag;
    // [range.split.outer.value], class split_­view​::​outer_­iterator​::​value_­type
    struct value_type;
    using difference_type   = range_difference_t<Base>;

    outer_iterator() = default;
    constexpr explicit outer_iterator(Parent& parent)
      requires (!forward_range<Base>);
    constexpr outer_iterator(Parent& parent, iterator_t<Base> current)
      requires forward_range<Base>;
    constexpr outer_iterator(outer_iterator<!Const> i)
      requires Const && convertible_to<iterator_t<V>, iterator_t<const V>>;

    constexpr value_type operator*() const;

    constexpr outer_iterator& operator++();
    constexpr decltype(auto) operator++(int) {
      if constexpr (forward_range<Base>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const outer_iterator& x, const outer_iterator& y)
      requires forward_range<Base>;

    friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
  };
}
Many of the following specifications refer to the notional member current of outer_­iterator.
current is equivalent to current_­ if V models forward_­range, and parent_­->current_­ otherwise.
constexpr explicit outer_iterator(Parent& parent) requires (!forward_range<Base>);
Effects: Initializes parent_­ with addressof(parent).
constexpr outer_iterator(Parent& parent, iterator_t<Base> current) requires forward_range<Base>;
Effects: Initializes parent_­ with addressof(parent) and current_­ with current.
constexpr outer_iterator(outer_iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, iterator_t<const V>>;
Effects: Initializes parent_­ with i.parent_­ and current_­ with std​::​move(i.current_­).
constexpr value_type operator*() const;
Effects: Equivalent to: return value_­type{*this};
constexpr outer_iterator& operator++();
Effects: Equivalent to:
const auto end = ranges::end(parent_->base_);
if (current == end) return *this;
const auto [pbegin, pend] = subrange{parent_->pattern_};
if (pbegin == pend) ++current;
else {
  do {
    const auto [b, p] = ranges::mismatch(current, end, pbegin, pend);
    if (p == pend) {
      current = b;  // The pattern matched; skip it
      break;
    }
  } while (++current != end);
}
return *this;
friend constexpr bool operator==(const outer_iterator& x, const outer_iterator& y) requires forward_range<Base>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
Effects: Equivalent to: return x.current == ranges​::​end(x.parent_­->base_­);

24.7.11.4 Class split_­view​::​outer_­iterator​::​value_­type [range.split.outer.value]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::outer_iterator<Const>::value_type {
  private:
    outer_iterator i_ = outer_iterator();               // exposition only
  public:
    value_type() = default;
    constexpr explicit value_type(outer_iterator i);

    constexpr inner_iterator<Const> begin() const;
    constexpr default_sentinel_t end() const;
  };
}
constexpr explicit value_type(outer_iterator i);
Effects: Initializes i_­ with i.
constexpr inner_iterator<Const> begin() const;
Effects: Equivalent to: return inner_­iterator<Const>{i_­};
constexpr default_sentinel_t end() const;
Effects: Equivalent to: return default_­sentinel;

24.7.11.5 Class template split_­view​::​inner_­iterator [range.split.inner]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::inner_iterator {
  private:
    using Base =
      conditional_t<Const, const V, V>;                 // exposition only
    outer_iterator<Const> i_ = outer_iterator<Const>(); // exposition only
    bool incremented_ = false;                          // exposition only
  public:
    using iterator_concept  = typename outer_iterator<Const>::iterator_concept;
    using iterator_category = see below;
    using value_type        = range_value_t<Base>;
    using difference_type   = range_difference_t<Base>;

    inner_iterator() = default;
    constexpr explicit inner_iterator(outer_iterator<Const> i);

    constexpr decltype(auto) operator*() const { return *i_.current; }

    constexpr inner_iterator& operator++();
    constexpr decltype(auto) operator++(int) {
      if constexpr (forward_range<V>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const inner_iterator& x, const inner_iterator& y)
      requires forward_range<Base>;

    friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);

    friend constexpr decltype(auto) iter_move(const inner_iterator& i)
    noexcept(noexcept(ranges::iter_move(i.i_.current))) {
      return ranges::iter_move(i.i_.current);
    }

    friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.i_.current, y.i_.current)))
      requires indirectly_swappable<iterator_t<Base>>;
  };
}
The typedef-name iterator_­category denotes forward_­iterator_­tag if iterator_­traits<iterator_­t<Base>>​::​iterator_­category models derived_­from<forward_­iterator_­tag>, and input_­iterator_­tag otherwise.
constexpr explicit inner_iterator(outer_iterator<Const> i);
Effects: Initializes i_­ with i.
constexpr inner_iterator& operator++() const;
Effects: Equivalent to:
incremented_ = true;
if constexpr (!forward_range<Base>) {
  if constexpr (Pattern::size() == 0) {
    return *this;
  }
}
++i_.current;
return *this;
friend constexpr bool operator==(const inner_iterator& x, const inner_iterator& y) requires forward_range<Base>;
Effects: Equivalent to: return x.i_­.current_­ == y.i_­.current_­;
friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
Effects: Equivalent to:
auto cur = x.i_.current;
auto end = ranges::end(x.i_.parent_->base_);
if (cur == end) return true;
auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
if (pcur == pend) return x.incremented_;
do {
  if (*cur != *pcur) return false;
  if (++pcur == pend) return true;
} while (++cur != end);
return false;
friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y) noexcept(noexcept(ranges::iter_swap(x.i_.current, y.i_.current))) requires indirectly_swappable<iterator_t<Base>>;
Effects: Equivalent to ranges​::​iter_­swap(x.i_­.current, y.i_­.current).

24.7.11.6 views​::​split [range.split.adaptor]

The name views​::​split denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression views​::​split(E, F) is expression-equivalent to split_­view{E, F}.

24.7.12 Counted view [range.counted]

A counted view presents a view of the elements of the counted range ([iterator.requirements.general]) [i, n) for some iterator i and non-negative integer n.
The name views​::​counted denotes a customization point object ([customization.point.object]).
Let E and F be expressions, and let T be decay_­t<decltype((E))>.
Then the expression views​::​counted(E, F) is expression-equivalent to:
  • If T models input_­or_­output_­iterator and decltype((F)) models convertible_­to<iter_­difference_­t<T>>,
    • subrange{E, E + static_­cast<iter_­difference_­t<T>>(F)} if T models random_­access_­iterator.
    • Otherwise, subrange{counted_­iterator{E, F}, default_­sentinel}.
  • Otherwise, views​::​counted(E, F) is ill-formed.
    Note
    : This case can result in substitution failure when views​::​counted(E, F) appears in the immediate context of a template instantiation. — end note
     ]

24.7.13 Common view [range.common]

24.7.13.1 Overview [range.common.overview]

common_­view takes a view which has different types for its iterator and sentinel and turns it into a view of the same elements with an iterator and sentinel of the same type.
Note
:
common_­view is useful for calling legacy algorithms that expect a range's iterator and sentinel types to be the same.
— end note
 ]
Example
:
// Legacy algorithm:
template<class ForwardIterator>
size_t count(ForwardIterator first, ForwardIterator last);

template<forward_range R>
void my_algo(R&& r) {
  auto&& common = common_view{r};
  auto cnt = count(common.begin(), common.end());
  // ...
}
— end example
 ]

24.7.13.2 Class template common_­view [range.common.view]

namespace std::ranges {
  template<view V>
    requires (!common_range<V>)
  class common_view : public view_interface<common_view<V>> {
  private:
    V base_ = V();  // exposition only
  public:
    common_view() = default;

    constexpr explicit common_view(V r);

    template<viewable_range R>
      requires (!common_range<R> && constructible_from<V, all_view<R>>)
    constexpr explicit common_view(R&& r);

    constexpr V base() const;

    constexpr auto size() requires sized_range<V> {
      return ranges::size(base_);
    }
    constexpr auto size() const requires sized_range<const V> {
      return ranges::size(base_);
    }

    constexpr auto begin() {
      if constexpr (random_access_range<V> && sized_range<V>)
        return ranges::begin(base_);
      else
        return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::begin(base_));
    }

    constexpr auto begin() const requires range<const V> {
      if constexpr (random_access_range<const V> && sized_range<const V>)
        return ranges::begin(base_);
      else
        return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::begin(base_));
    }

    constexpr auto end() {
      if constexpr (random_access_range<V> && sized_range<V>)
        return ranges::begin(base_) + ranges::size(base_);
      else
        return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::end(base_));
    }

    constexpr auto end() const requires range<const V> {
      if constexpr (random_access_range<const V> && sized_range<const V>)
        return ranges::begin(base_) + ranges::size(base_);
      else
        return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::end(base_));
    }
  };

  template<class R>
    common_view(R&&) -> common_view<all_view<R>>;
}
constexpr explicit common_view(V base);
Effects: Initializes base_­ with std​::​move(base).
template<viewable_range R> requires (!common_range<R> && constructible_from<V, all_view<R>>) constexpr explicit common_view(R&& r);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)).
constexpr V base() const;
Effects: Equivalent to: return base_­;

24.7.13.3 views​::​common [range.common.adaptor]

The name views​::​common denotes a range adaptor object ([range.adaptor.object]).
For some subexpression E, the expression views​::​common(E) is expression-equivalent to:
  • views​::​all(E), if decltype((E)) models common_­range and views​::​all(E) is a well-formed expression.
  • Otherwise, common_­view{E}.

24.7.14 Reverse view [range.reverse]

24.7.14.1 Overview [range.reverse.overview]

reverse_­view takes a bidirectional view and produces another view that iterates the same elements in reverse order.
Example
:
vector<int> is {0,1,2,3,4};
reverse_view rv {is};
for (int i : rv)
  cout << i << ' '; // prints: 4 3 2 1 0
— end example
 ]

24.7.14.2 Class template reverse_­view [range.reverse.view]

namespace std::ranges {
  template<view V>
    requires bidirectional_range<V>
  class reverse_view : public view_interface<reverse_view<V>> {
  private:
    V base_ = V();  // exposition only
  public:
    reverse_view() = default;

    constexpr explicit reverse_view(V r);

    template<viewable_range R>
      requires bidirectional_range<R> && constructible_from<V, all_view<R>>
    constexpr explicit reverse_view(R&& r);

    constexpr V base() const;

    constexpr reverse_iterator<iterator_t<V>> begin();
    constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>;
    constexpr reverse_iterator<iterator_t<const V>> begin() const
      requires common_range<const V>;

    constexpr reverse_iterator<iterator_t<V>> end();
    constexpr reverse_iterator<iterator_t<const V>> end() const
      requires common_range<const V>;

    constexpr auto size() requires sized_range<V> {
      return ranges::size(base_);
    }
    constexpr auto size() const requires sized_range<const V> {
      return ranges::size(base_);
    }
  };

  template<class R>
    reverse_view(R&&) -> reverse_view<all_view<R>>;
}
constexpr explicit reverse_view(V base);
Effects: Initializes base_­ with std​::​move(base).
template<viewable_range R> requires bidirectional_range<R> && constructible_from<V, all_view<R>> constexpr explicit reverse_view(R&& r);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)).
constexpr V base() const;
Effects: Equivalent to: return base_­;
constexpr reverse_iterator<iterator_t<V>> begin();
Returns:
make_reverse_iterator(ranges::next(ranges::begin(base_), ranges::end(base_)))
Remarks: In order to provide the amortized constant time complexity required by the range concept, this function caches the result within the reverse_­view for use on subsequent calls.
constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>; constexpr reverse_iterator<iterator_t<const V>> begin() const requires common_range<const V>;
Effects: Equivalent to: return make_­reverse_­iterator(ranges​::​end(base_­));
constexpr reverse_iterator<iterator_t<V>> end(); constexpr reverse_iterator<iterator_t<const V>> end() const requires common_range<const V>;
Effects: Equivalent to: return make_­reverse_­iterator(ranges​::​begin(base_­));

24.7.14.3 views​::​reverse [range.reverse.adaptor]

The name views​::​reverse denotes a range adaptor object ([range.adaptor.object]).
For some subexpression E, the expression views​::​reverse(E) is expression-equivalent to:
  • If the type of E is a (possibly cv-qualified) specialization of reverse_­view, equivalent to E.base().
  • Otherwise, if the type of E is cv-qualified
    subrange<reverse_iterator<I>, reverse_iterator<I>, K>
    
    for some iterator type I and value K of type subrange_­kind,
    • if K is subrange_­kind​::​sized, equivalent to:
      subrange<I, I, K>(E.end().base(), E.begin().base(), E.size())
      
    • otherwise, equivalent to:
      subrange<I, I, K>(E.end().base(), E.begin().base())
      
    However, in either case E is evaluated only once.
  • Otherwise, equivalent to reverse_­view{E}.

24.7.15 Istream view [range.istream]

24.7.15.1 Overview [range.istream.overview]

basic_­istream_­view models input_­range and reads (using operator>>) successive elements from its corresponding input stream.
Example
:
auto ints = istringstream{"0 1  2   3     4"};
ranges::copy(istream_view<int>(ints), ostream_iterator<int>{cout, "-"});
// prints 0-1-2-3-4-
— end example
 ]

24.7.15.2 Class template basic_­istream_­view [range.istream.view]

namespace std::ranges {
  template<class Val, class CharT, class Traits>
    concept stream-extractable =                // exposition only
      requires(basic_istream<CharT, Traits>& is, Val& t) {
         is >> t;
      };

  template<movable Val, class CharT, class Traits>
    requires default_constructible<Val> &&
      stream-extractable<Val, CharT, Traits>
  class basic_istream_view : public view_interface<basic_istream_view<Val, CharT, Traits>> {
  public:
    basic_istream_view() = default;
    constexpr explicit basic_istream_view(basic_istream<CharT, Traits>& stream);

    constexpr auto begin()
    {
      if (stream_) {
        *stream_ >> object_;
      }
      return iterator{*this};
    }

    constexpr default_sentinel_t end() const noexcept;

  private:
    struct iterator;                                    // exposition only
    basic_istream<CharT, Traits>* stream_{};            // exposition only
    Val object_ = Val();                                // exposition only
  };
}
constexpr explicit basic_istream_view(basic_istream<CharT, Traits>& stream);
Effects: Initializes stream_­ with addressof(stream).
constexpr default_sentinel_t end() const noexcept;
Effects: Equivalent to: return default_­sentinel;
template<class Val, class CharT, class Traits> basic_istream_view<Val, CharT, Traits> istream_view(basic_istream<CharT, Traits>& s);
Effects: Equivalent to: return basic_­istream_­view<Val, CharT, Traits>{s};

24.7.15.3 Class template basic_­istream_­view​::​iterator [range.istream.iterator]

namespace std::ranges {
  template<class Val, class CharT, class Traits>
  class basic_istream_view<Val, CharT, Traits>::iterator {      // exposition only
  public:
    using iterator_category = input_iterator_tag;
    using difference_type = ptrdiff_t;
    using value_type = Val;

    iterator() = default;
    constexpr explicit iterator(basic_istream_view& parent) noexcept;

    iterator(const iterator&) = delete;
    iterator(iterator&&) = default;

    iterator& operator=(const iterator&) = delete;
    iterator& operator=(iterator&&) = default;

    iterator& operator++();
    void operator++(int);

    Val& operator*() const;

    friend bool operator==(const iterator& x, default_sentinel_t);

  private:
    basic_istream_view* parent_{};                              // exposition only
  };
}
constexpr explicit iterator(basic_istream_view& parent) noexcept;
Effects: Initializes parent_­ with addressof(parent).
iterator& operator++();
Expects: parent_­->stream_­ != nullptr is true.
Effects: Equivalent to:
*parent_->stream >> parent_->object_;
return *this;
void operator++(int);
Expects: parent_­->stream_­ != nullptr is true.
Effects: Equivalent to ++*this.
Val& operator*() const;
Expects: parent_­->stream_­ != nullptr is true.
Effects: Equivalent to: return parent_­->value_­;
friend bool operator==(const iterator& x, default_sentinel_t);
Effects: Equivalent to: return x.parent_­ == nullptr || !*x.parent_­->stream_­;

24.7.16 Elements view [range.elements]

24.7.16.1 Overview [range.elements.overview]

elements_­view takes a view of tuple-like values and a size_­t, and produces a view with a value-type of the element of the adapted view's value-type.
The name views​::​elements<N> denotes a range adaptor object ([range.adaptor.object]).
For some subexpression E and constant expression N, the expression views​::​elements<N>(E) is expression-equivalent to elements_­view<all_­view<decltype((E))>, N>{E}.
Example
:
auto historical_figures = map{
  {"Lovelace"sv, 1815},
  {"Turing"sv, 1912},
  {"Babbage"sv, 1791},
  {"Hamilton"sv, 1936}
};

auto names = historical_figures | views::elements<0>;
for (auto&& name : names) {
  cout << name << ' ';          // prints Babbage Hamilton Lovelace Turing 
}

auto birth_years = historical_figures | views::elements<1>;
for (auto&& born : birth_years) {
  cout << born << ' ';          // prints 1791 1936 1815 1912 
}
— end example
 ]
keys_­view is an alias for elements_­view<all_­view<R>, 0>, and is useful for extracting keys from associative containers.
Example
:
auto names = keys_view{historical_figures};
for (auto&& name : names) {
  cout << name << ' ';          // prints Babbage Hamilton Lovelace Turing 
}
— end example
 ]
values_­view is an alias for elements_­view<all_­view<R>, 1>, and is useful for extracting values from associative containers.
Example
:
auto is_even = [](const auto x) { return x % 2 == 0; };
cout << ranges::count_if(values_view{historical_figures}, is_even);     // prints 2
— end example
 ]

24.7.16.2 Class template elements_­view [range.elements.view]

namespace std::ranges {
  template<class T, size_t N>
  concept has-tuple-element =                   // exposition only
    requires(T t) {
      typename tuple_size<T>::type;
      requires N < tuple_size_v<T>;
      typename tuple_element_t<N, T>;
      { get<N>(t) } -> const tuple_element_t<N, T>&;
    };


  template<input_range R, size_t N>
    requires view<R> && has-tuple-element<range_value_t<R>, N> &&
      has-tuple-element<remove_reference_t<range_reference_t<R>>, N>
  class elements_view : public view_interface<elements_view<R, N>> {
  public:
    elements_view() = default;
    constexpr explicit elements_view(R base);

    constexpr R base() const;

    constexpr auto begin() requires (!simple-view<R>)
    { return iterator<false>(ranges::begin(base_)); }

    constexpr auto begin() const requires simple-view<R>
    { return iterator<true>(ranges::begin(base_)); }

    constexpr auto end() requires (!simple-view<R>)
    { return ranges::end(base_); }

    constexpr auto end() const requires simple-view<R>
    { return ranges::end(base_); }

    constexpr auto size() requires sized_range<R>
    { return ranges::size(base_); }

    constexpr auto size() const requires sized_range<const R>
    { return ranges::size(base_); }

  private:
    template<bool> struct iterator;                     // exposition only
    R base_ = R();                                      // exposition only
  };
}
constexpr explicit elements_view(R base);
Effects: Initializes base_­ with std​::​move(base).
constexpr R base() const;
Effects: Equivalent to: return base_­;

24.7.16.3 Class template elements_­view​::​iterator [range.elements.iterator]

namespace std::ranges {
  template<class R, size_t N>
  template<bool Const>
  class elements_view<R, N>::iterator {                 // exposition only
    using base_t = conditional_t<Const, const R, R>;
    friend iterator<!Const>;

    iterator_t<base_t> current_;
  public:
    using iterator_category = typename iterator_traits<iterator_t<base_t>>::iterator_category;
    using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<base_t>>>;
    using difference_type = range_difference_t<base_t>;

    iterator() = default;
    constexpr explicit iterator(iterator_t<base_t> current);
    constexpr iterator(iterator<!Const> i)
      requires Const && convertible_to<iterator_t<R>, iterator_t<base_t>>;

    constexpr iterator_t<base_t> base() const;

    constexpr decltype(auto) operator*() const
    { return get<N>(*current_); }

    constexpr iterator& operator++();
    constexpr void operator++(int) requires (!forward_range<base_t>);
    constexpr iterator operator++(int) requires forward_range<base_t>;

    constexpr iterator& operator--() requires bidirectional_range<base_t>;
    constexpr iterator operator--(int) requires bidirectional_range<base_t>;

    constexpr iterator& operator+=(difference_type x)
      requires random_access_range<base_t>;
    constexpr iterator& operator-=(difference_type x)
      requires random_access_range<base_t>;

    constexpr decltype(auto) operator[](difference_type n) const
      requires random_access_range<base_t>
    { return get<N>(*(current_ + n)); }

    friend constexpr bool operator==(const iterator& x, const iterator& y)
      requires equality_comparable<iterator_t<base_t>>;
    friend constexpr bool operator==(const iterator& x, const sentinel_t<base_t>& y);

    friend constexpr bool operator<(const iterator& x, const iterator& y)
      requires random_access_range<base_t>;
    friend constexpr bool operator>(const iterator& x, const iterator& y)
      requires random_access_range<base_t>;
    friend constexpr bool operator<=(const iterator& y, const iterator& y)
      requires random_access_range<base_t>;
    friend constexpr bool operator>=(const iterator& x, const iterator& y)
      requires random_access_range<base_t>;
    friend constexpr compare_three_way_result_t<iterator_t<base_t>>
      operator<=>(const iterator& x, const iterator& y)
        requires random_access_range<base_t> && three_way_comparable<iterator_t<base_t>>;

    friend constexpr iterator operator+(const iterator& x, difference_type y)
      requires random_access_range<base_t>;
    friend constexpr iterator operator+(difference_type x, const iterator& y)
      requires random_access_range<base_t>;
    friend constexpr iterator operator-(const iterator& x, difference_type y)
      requires random_access_range<base_t>;
    friend constexpr difference_type operator-(const iterator& x, const iterator& y)
      requires random_access_range<base_t>;

    friend constexpr difference_type
      operator-(const iterator<Const>& x, const sentinel_t<base_t>& y)
        requires sized_sentinel_for<sentinel_t<base_t>, iterator_t<base_t>>;
    friend constexpr difference_type
      operator-(const sentinel_t<base_t>& x, const iterator<Const>& y)
        requires sized_sentinel_for<sentinel_t<base_t>, iterator_t<base_t>>;
  };
}
constexpr explicit iterator(iterator_t<base_t> current);
Effects: Initializes current_­ with current.
constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<R>, iterator_t<base_t>>;
Effects: Initializes current_­ with i.current_­.
constexpr iterator_t<base_t> base() const;
Effects: Equivalent to: return current_­;
constexpr iterator& operator++();
Effects: Equivalent to:
++current_;
return *this;
constexpr void operator++(int) requires (!forward_range<base_t>);
Effects: Equivalent to: ++current_­.
constexpr iterator operator++(int) requires forward_range<base_t>;
Effects: Equivalent to:
auto temp = *this;
++current_;
return temp;
constexpr iterator& operator--() requires bidirectional_range<base_t>;
Effects: Equivalent to:
--current_;
return *this;
constexpr iterator operator--(int) requires bidirectional_range<base_t>;
Effects: Equivalent to:
auto temp = *this;
--current_;
return temp;
constexpr iterator& operator+=(difference_type n); requires random_access_range<base_t>;
Effects: Equivalent to:
current_ += n;
return *this;
constexpr iterator& operator-=(difference_type n) requires random_access_range<base_t>;
Effects: Equivalent to:
current_ -= n;
return *this;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_comparable<base_t>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator==(const iterator& x, const sentinel_t<base_t>& y);
Effects: Equivalent to: return x.current_­ == y;
friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return x.current_­ < y.current_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return !(x < y);
friend constexpr compare_three_way_result_t<iterator_t<base_t>> operator<=>(const iterator& x, const iterator& y) requires random_access_range<base_t> && three_way_comparable<iterator_t<base_t>>;
Effects: Equivalent to: return x.current_­ <=> y.current_­;
friend constexpr iterator operator+(const iterator& x, difference_type y) requires random_access_range<base_t>;
Effects: Equivalent to: return iterator{x} += y;
friend constexpr iterator operator+(difference_type x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return y + x;
constexpr iterator operator-(const iterator& x, difference_type y) requires random_access_range<base_t>;
Effects: Equivalent to: return iterator{x} -= y;
constexpr difference_type operator-(const iterator& x, const iterator& y) requires random_access_range<base_t>;
Effects: Equivalent to: return x.current_­ - y.current_­;
friend constexpr difference_type operator-(const iterator<Const>& x, const sentinel_t<base_t>& y) requires sized_sentinel_for<sentinel_t<base_t>, iterator_t<base_t>>;
Effects: Equivalent to: return x.current_­ - y;
friend constexpr difference_type operator-(const sentinel_t<base_t>& x, const iterator<Const>& y) requires sized_sentinel_for<sentinel_t<base_t>, iterator_t<base_t>>;
Effects: Equivalent to: return -(y - x);