24 Ranges library [ranges]

24.1 General [ranges.general]

This clause describes components for dealing with ranges of elements.
The following subclauses describe range and view requirements, and components for range primitives as summarized in Table 79.
Table 79 — Ranges library summary
Subclause
Header(s)
Range access
<ranges>
Requirements
Range utilities
Range factories
Range adaptors

24.2 Header <ranges> synopsis [ranges.syn]

#include <initializer_list>
#include <iterator>

namespace std::ranges {
  inline namespace unspecified {
    // [range.access], range access
    inline constexpr unspecified begin = unspecified;
    inline constexpr unspecified end = unspecified;
    inline constexpr unspecified cbegin = unspecified;
    inline constexpr unspecified cend = unspecified;
    inline constexpr unspecified rbegin = unspecified;
    inline constexpr unspecified rend = unspecified;
    inline constexpr unspecified crbegin = unspecified;
    inline constexpr unspecified crend = unspecified;

    inline constexpr unspecified size = unspecified;
    inline constexpr unspecified empty = unspecified;
    inline constexpr unspecified data = unspecified;
    inline constexpr unspecified cdata = unspecified;
  }

  // [range.range], ranges
  template<class T>
    using iterator_t = decltype(ranges::begin(declval<T&>()));

  template<class T>
    using sentinel_t = decltype(ranges::end(declval<T&>()));

  template<class T>
    concept Range = see below;

  // [range.sized], sized ranges
  template<class>
    inline constexpr bool disable_sized_range = false;

  template<class T>
    concept SizedRange = see below;

  // [range.view], views
  template<class T>
    inline constexpr bool enable_view = see below;

  struct view_base { };

  template<class T>
    concept View = see below;

  // [range.refinements], other range refinements
  template<class R, class T>
    concept OutputRange = see below;

  template<class T>
    concept InputRange = see below;

  template<class T>
    concept ForwardRange = see below;

  template<class T>
    concept BidirectionalRange = see below;

  template<class T>
    concept RandomAccessRange = see below;

  template<class T>
    concept ContiguousRange = see below;

  template<class T>
    concept CommonRange = see below;

  template<class T>
    concept ViewableRange = see below;

  // [view.interface], class template view_­interface
  template<class D>
    requires is_class_v<D> && Same<D, remove_cv_t<D>>
  class view_interface;

  // [range.subrange], sub-ranges
  enum class subrange_kind : bool { unsized, sized };

  template<Iterator I, Sentinel<I> S = I, subrange_kind K = see below>
    requires (K == subrange_kind::sized || !SizedSentinel<S, I>)
  class subrange;

  // [range.dangling], dangling iterator handling
  struct dangling;

  template<Range R>
    using safe_iterator_t = conditional_t<forwarding-range<R>, iterator_t<R>, dangling>;

  template<Range R>
    using safe_subrange_t =
      conditional_t<forwarding-range<R>, subrange<iterator_t<R>>, dangling>;

  // [range.empty], empty view
  template<class T>
    requires is_object_v<T>
  class empty_view;

  namespace view {
    template<class T>
      inline constexpr empty_view<T> empty{};
  }

  // [range.single], single view
  template<CopyConstructible T>
    requires is_object_v<T>
  class single_view;

  namespace view { inline constexpr unspecified single = unspecified; }

  // [range.iota], iota view
  template<WeaklyIncrementable W, Semiregular Bound = unreachable_sentinel_t>
    requires weakly-equality-comparable-with<W, Bound>
  class iota_view;

  namespace view { inline constexpr unspecified iota = unspecified; }

  // [range.all], all view
  namespace view { inline constexpr unspecified all = unspecified; }

  template<ViewableRange R>
    using all_view = decltype(view::all(declval<R>()));

  template<Range R>
    requires is_object_v<R>
  class ref_view;

  // [range.filter], filter view
  template<InputRange V, IndirectUnaryPredicate<iterator_t<V>> Pred>
    requires View<V> && is_object_v<Pred>
  class filter_view;

  namespace view { inline constexpr unspecified filter = unspecified; }

  // [range.transform], transform view
  template<InputRange V, CopyConstructible F>
    requires View<V> && is_object_v<F> &&
             RegularInvocable<F&, iter_reference_t<iterator_t<V>>>
  class transform_view;

  namespace view { inline constexpr unspecified transform = unspecified; }

  // [range.take], take view
  template<View> class take_view;

  namespace view { inline constexpr unspecified take = unspecified; }

  // [range.join], join view
  template<InputRange V>
    requires View<V> && InputRange<iter_reference_t<iterator_t<V>>> &&
             (is_reference_v<iter_reference_t<iterator_t<V>>> ||
              View<iter_value_t<iterator_t<V>>>)
  class join_view;

  namespace view { inline constexpr unspecified join = unspecified; }

  // [range.split], split view
  template<class R>
    concept tiny-range = see below;   // exposition only

  template<InputRange V, ForwardRange Pattern>
    requires View<V> && View<Pattern> &&
             IndirectlyComparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (ForwardRange<V> || tiny-range<Pattern>)
  class split_view;

  namespace view { inline constexpr unspecified split = unspecified; }

  // [range.counted], counted view
  namespace view { inline constexpr unspecified counted = unspecified; }

  // [range.common], common view
  template<View V>
    requires (!CommonRange<V>)
  class common_view;

  namespace view { inline constexpr unspecified common = unspecified; }

  // [range.reverse], reverse view
  template<View V>
    requires BidirectionalRange<V>
  class reverse_view;

  namespace view { inline constexpr unspecified reverse = unspecified; }
}

namespace std {
  namespace view = ranges::view;

  template<class I, class S, ranges::subrange_kind K>
  struct tuple_size<ranges::subrange<I, S, K>>
    : integral_constant<size_t, 2> {};
  template<class I, class S, ranges::subrange_kind K>
  struct tuple_element<0, ranges::subrange<I, S, K>> {
    using type = I;
  };
  template<class I, class S, ranges::subrange_kind K>
  struct tuple_element<1, ranges::subrange<I, S, K>> {
    using type = S;
  };
}

24.3 Range access [range.access]

In addition to being available via inclusion of the <ranges> header, the customization point objects in [range.access] are available when <iterator> is included.

24.3.1 ranges​::​begin [range.access.begin]

The name ranges::begin denotes a customization point object.
The expression ranges::​begin(E) for some subexpression E is expression-equivalent to:
  • E + 0 if E is an lvalue of array type ([basic.compound]).
  • Otherwise, if E is an lvalue, decay-copy(E.begin()) if it is a valid expression and its type I models Iterator.
  • Otherwise, decay-copy(begin(E)) if it is a valid expression and its type I models Iterator with overload resolution performed in a context that includes the declarations:
    template<class T> void begin(T&&) = delete;
    template<class T> void begin(initializer_list<T>&&) = delete;
      
    and does not include a declaration of ranges::begin.
  • Otherwise, ranges::begin(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::begin(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::begin(E) is a valid expression, its type models Iterator.
end note
]

24.3.2 ranges​::​end [range.access.end]

The name ranges::end denotes a customization point object.
The expression ranges::end(E) for some subexpression E is expression-equivalent to:
  • E + extent_­v<T> if E is an lvalue of array type ([basic.compound]) T.
  • Otherwise, if E is an lvalue, decay-copy(E.end()) if it is a valid expression and its type S models Sentinel<decltype(​ranges::​begin(E))>.
  • Otherwise, decay-copy(end(E)) if it is a valid expression and its type S models Sentinel<decltype(​ranges::​begin(E))> with overload resolution performed in a context that includes the declarations:
    template<class T> void end(T&&) = delete;
    template<class T> void end(initializer_list<T>&&) = delete;
      
    and does not include a declaration of ranges::end.
  • Otherwise, ranges::end(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::end(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::end(E) is a valid expression, the types S and I of ranges::end(E) and ranges::begin(E) model Sentinel<S, I>.
end note
]

24.3.3 ranges​::​cbegin [range.access.cbegin]

The name ranges::cbegin denotes a customization point object.
The expression ranges::​cbegin(E) for some subexpression E of type T is expression-equivalent to:
  • ranges::begin(static_­cast<const T&>(E)) if E is an lvalue.
  • Otherwise, ranges::begin(static_­cast<const T&&>(E)).
[Note
:
Whenever ranges::cbegin(E) is a valid expression, its type models Iterator.
end note
]

24.3.4 ranges​::​cend [range.access.cend]

The name ranges::cend denotes a customization point object.
The expression ranges::cend(E) for some subexpression E of type T is expression-equivalent to:
  • ranges::end(static_­cast<const T&>(E)) if E is an lvalue.
  • Otherwise, ranges::end(static_­cast<const T&&>(E)).
[Note
:
Whenever ranges::cend(E) is a valid expression, the types S and I of ranges::cend(E) and ranges::cbegin(E) model Sentinel<S, I>.
end note
]

24.3.5 ranges​::​rbegin [range.access.rbegin]

The name ranges::rbegin denotes a customization point object.
The expression ranges::​rbegin(E) for some subexpression E is expression-equivalent to:
  • If E is an lvalue, decay-copy(E.rbegin()) if it is a valid expression and its type I models Iterator.
  • Otherwise, decay-copy(rbegin(E)) if it is a valid expression and its type I models Iterator with overload resolution performed in a context that includes the declaration:
    template<class T> void rbegin(T&&) = delete;
      
    and does not include a declaration of ranges::rbegin.
  • Otherwise, make_­reverse_­iterator(ranges::end(E)) if both ranges::begin(E) and ranges::end(​E) are valid expressions of the same type I which models BidirectionalIterator.
  • Otherwise, ranges::rbegin(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::rbegin(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::rbegin(E) is a valid expression, its type models Iterator.
end note
]

24.3.6 ranges​::​rend [range.access.rend]

The name ranges::rend denotes a customization point object.
The expression ranges::rend(E) for some subexpression E is expression-equivalent to:
  • If E is an lvalue, decay-copy(E.rend()) if it is a valid expression and its type S models Sentinel<decltype(ranges::rbegin(E))>.
  • Otherwise, decay-copy(rend(E)) if it is a valid expression and its type S models Sentinel<decltype(​ranges::rbegin(E))> with overload resolution performed in a context that includes the declaration:
    template<class T> void rend(T&&) = delete;
      
    and does not include a declaration of ranges::rend.
  • Otherwise, make_­reverse_­iterator(ranges::begin(E)) if both ranges::begin(E) and ranges::​end(E) are valid expressions of the same type I which models BidirectionalIterator.
  • Otherwise, ranges::rend(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::rend(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::rend(E) is a valid expression, the types S and I of ranges::rend(E) and ranges::rbegin(E) model Sentinel<S, I>.
end note
]

24.3.7 ranges​::​crbegin [range.access.crbegin]

The name ranges::crbegin denotes a customization point object.
The expression ranges::​crbegin(E) for some subexpression E of type T is expression-equivalent to:
  • ranges::​rbegin(static_­cast<const T&>(E)) if E is an lvalue.
  • Otherwise, ranges::rbegin(static_­cast<const T&&>(E)).
[Note
:
Whenever ranges::crbegin(E) is a valid expression, its type models Iterator.
end note
]

24.3.8 ranges​::​crend [range.access.crend]

The name ranges::crend denotes a customization point object.
The expression ranges::​crend(E) for some subexpression E of type T is expression-equivalent to:
  • ranges::rend(static_­cast<const T&>(E)) if E is an lvalue.
  • Otherwise, ranges::rend(static_­cast<const T&&>(E)).
[Note
:
Whenever ranges::crend(E) is a valid expression, the types S and I of ranges::crend(E) and ranges::crbegin(E) model Sentinel<S, I>.
end note
]

24.3.9 ranges​::​size [range.prim.size]

The name size denotes a customization point object.
The expression ranges::size(E) for some subexpression E with type T is expression-equivalent to:
  • decay-copy(extent_­v<T>) if T is an array type ([basic.compound]).
  • Otherwise, if disable_­sized_­range<remove_­cv_­t<T>> ([range.sized]) is false:
    • decay-copy(E.size()) if it is a valid expression and its type I models Integral.
    • Otherwise, decay-copy(size(E)) if it is a valid expression and its type I models Integral with overload resolution performed in a context that includes the declaration:
      template<class T> void size(T&&) = delete;
          
      and does not include a declaration of ranges::size.
  • Otherwise, (ranges::end(E) - ranges::begin(E)) if it is a valid expression and the types I and S of ranges::begin(E) and ranges::end(E) model SizedSentinel<S, I> ([iterator.concept.sizedsentinel]) and ForwardIterator<I>.
    However, E is evaluated only once.
  • Otherwise, ranges::size(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::size(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::size(E) is a valid expression, its type models Integral.
end note
]

24.3.10 ranges​::​empty [range.prim.empty]

The name empty denotes a customization point object.
The expression ranges::empty(E) for some subexpression E is expression-equivalent to:
  • bool((E).empty()) if it is a valid expression.
  • Otherwise, (ranges::size(E) == 0) if it is a valid expression.
  • Otherwise, EQ, where EQ is bool(ranges::begin(E) == ranges::end(E)) except that E is only evaluated once, if EQ is a valid expression and the type of ranges::begin(E) models ForwardIterator.
  • Otherwise, ranges::empty(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::empty(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::empty(E) is a valid expression, it has type bool.
end note
]

24.3.11 ranges​::​data [range.prim.data]

The name data denotes a customization point object.
The expression ranges::data(E) for some subexpression E is expression-equivalent to:
  • If E is an lvalue, decay-copy(E.data()) if it is a valid expression of pointer to object type.
  • Otherwise, if ranges::begin(E) is a valid expression whose type models ContiguousIterator,
    ranges::begin(E) == ranges::end(E) ? nullptr : addressof(*ranges::begin(E))
      
    except that E is evaluated only once.
  • Otherwise, ranges::data(E) is ill-formed.
    [Note
    :
    This case can result in substitution failure when ranges::data(E) appears in the immediate context of a template instantiation.
    end note
    ]
[Note
:
Whenever ranges::data(E) is a valid expression, it has pointer to object type.
end note
]

24.3.12 ranges​::​cdata [range.prim.cdata]

The name cdata denotes a customization point object.
The expression ranges::cdata(E) for some subexpression E of type T is expression-equivalent to:
  • ranges::data(static_­cast<const T&>(E)) if E is an lvalue.
  • Otherwise, ranges::data(static_­cast<const T&&>(E)).
[Note
:
Whenever ranges::cdata(E) is a valid expression, it has pointer to object type.
end note
]

24.4 Range requirements [range.req]

24.4.1 General [range.req.general]

Ranges are an abstraction that allow a C++ program to operate on elements of data structures uniformly.
Calling ranges::begin on a range returns an object whose type models Iterator.
Calling ranges::end on a range returns an object whose type S, together with the type I of the object returned by ranges::begin, models Sentinel<S, I>.
The library formalizes the interfaces, semantics, and complexity of ranges to enable algorithms and range adaptors that work efficiently on different types of sequences.
The Range concept requires that ranges::begin and ranges::end return an iterator and a sentinel, respectively.
The SizedRange concept refines Range with the requirement that the number of elements in the range can be determined in constant time using the ranges::size function.
The View concept specifies requirements on a Range type with constant-time copy and assign operations.
Several refinements of Range group requirements that arise frequently in concepts and algorithms.
Common ranges are ranges for which ranges::begin and ranges::end return objects of the same type.
Random access ranges are ranges for which ranges::begin returns a type that models RandomAccessIterator.
(Contiguous, bidirectional, forward, input, and output ranges are defined similarly.)
Viewable ranges can be converted to views.

24.4.2 Ranges [range.range]

The Range concept defines the requirements of a type that allows iteration over its elements by providing an iterator and sentinel that denote the elements of the range.
template<class T> concept range-impl = // exposition only requires(T&& t) { ranges::begin(std::forward<T>(t)); // sometimes equality-preserving (see below) ranges::end(std::forward<T>(t)); }; template<class T> concept Range = range-impl<T&>; template<class T> concept forwarding-range = // exposition only Range<T> && range-impl<T>;
The required expressions ranges::begin(std::forward<T>(t)) and ranges::end(std::forward<​T>(t)) of the range-impl concept do not require implicit expression variations ([concepts.equality]).
Given an expression E such that decltype((E)) is T, T models range-impl only if
  • [ranges::begin(E), ranges::end(E)) denotes a range ([iterator.requirements.general]),
  • both ranges::begin(E) and ranges::end(E) are amortized constant time and non-modifying, and
  • if the type of ranges::begin(E) models ForwardIterator, ranges::begin(E) is equality-preserving.
[Note
:
Equality preservation of both ranges::begin and ranges::end enables passing a Range whose iterator type models ForwardIterator to multiple algorithms and making multiple passes over the range by repeated calls to ranges::begin and ranges::end.
Since ranges::begin is not required to be equality-preserving when the return type does not model ForwardIterator, repeated calls might not return equal values or might not be well-defined; ranges::begin should be called at most once for such a range.
end note
]
Given an expression E such that decltype((E)) is T and an lvalue t that denotes the same object as E, T models forwarding-range only if
  • ranges::begin(E) and ranges::begin(t) are expression-equivalent,
  • ranges::end(E) and ranges::end(t) are expression-equivalent, and
  • the validity of iterators obtained from the object denoted by E is not tied to the lifetime of that object.
[Note
:
Since the validity of iterators is not tied to the lifetime of an object whose type models forwarding-range, a function can accept arguments of such a type by value and return iterators obtained from it without danger of dangling.
end note
]
[Example
:
Specializations of class template subrange model forwarding-range.
subrange provides non-member rvalue overloads of begin and end with the same semantics as its member lvalue overloads, and subrange's iterators - since they are “borrowed” from some other range - do not have validity tied to the lifetime of a subrange object.
end example
]

24.4.3 Sized ranges [range.sized]

The SizedRange concept specifies the requirements of a Range type that knows its size in constant time with the size function.
template<class T> concept SizedRange = Range<T> && !disable_sized_range<remove_cvref_t<T>> && requires(T& t) { ranges::size(t); };
Given an lvalue t of type remove_­reference_­t<T>, T models SizedRange only if
  • ranges::size(t) is , does not modify t, and is equal to ranges::distance(t), and
  • if iterator_­t<T> models ForwardIterator, ranges::size(t) is well-defined regardless of the evaluation of ranges::begin(t).
    [Note
    :
    ranges::size(t) is otherwise not required to be well-defined after evaluating ranges::begin(t).
    For example, ranges::size(t) might be well-defined for a SizedRange whose iterator type does not model ForwardIterator only if evaluated before the first call to ranges::begin(t).
    end note
    ]
[Note
:
The complexity requirement for the evaluation of ranges::size is non-amortized, unlike the case for the complexity of the evaluations of ranges::begin and ranges::end in the Range concept.
end note
]
[Note
:
disable_­sized_­range allows use of range types with the library that satisfy but do not in fact model SizedRange.
end note
]

24.4.4 Views [range.view]

The View concept specifies the requirements of a Range type that has constant time copy, move, and assignment operators; that is, the cost of these operations is not proportional to the number of elements in the View.
[Example
:
Examples of Views are:
  • A Range type that wraps a pair of iterators.
  • A Range type that holds its elements by shared_­ptr and shares ownership with all its copies.
  • A Range type that generates its elements on demand.
Most containers are not views since copying the container copies the elements, which cannot be done in constant time.
end example
]
template<class T> inline constexpr bool enable_view = see below; template<class T> concept View = Range<T> && Semiregular<T> && enable_view<T>;
Since the difference between Range and View is largely semantic, the two are differentiated with the help of enable_­view.
For a type T, the default value of enable_­view<T> is:
Pursuant to [namespace.std], users may specialize enable_­view to true for types which model View, and false for types which do not.

24.4.5 Other range refinements [range.refinements]

The OutputRange concept specifies requirements of a Range type for which ranges::begin returns a model of OutputIterator.
InputRange, ForwardRange, BidirectionalRange, and RandomAccessRange are defined similarly.
template<class R, class T> concept OutputRange = Range<R> && OutputIterator<iterator_t<R>, T>; template<class T> concept InputRange = Range<T> && InputIterator<iterator_t<T>>; template<class T> concept ForwardRange = InputRange<T> && ForwardIterator<iterator_t<T>>; template<class T> concept BidirectionalRange = ForwardRange<T> && BidirectionalIterator<iterator_t<T>>; template<class T> concept RandomAccessRange = BidirectionalRange<T> && RandomAccessIterator<iterator_t<T>>;
ContiguousRange additionally requires that the ranges::data customization point ([range.prim.data]) is usable with the range.
template<class T> concept ContiguousRange = RandomAccessRange<T> && ContiguousIterator<iterator_t<T>> && requires(T& t) { { ranges::data(t) } -> Same<add_pointer_t<iter_reference_t<iterator_t<T>>>>; };
The CommonRange concept specifies requirements of a Range type for which ranges::begin and ranges::end return objects of the same type.
[Example
:
The standard containers model CommonRange.
end example
]
template<class T> concept CommonRange = Range<T> && Same<iterator_t<T>, sentinel_t<T>>;
The ViewableRange concept specifies the requirements of a Range type that can be converted to a View safely.
template<class T> concept ViewableRange = Range<T> && (forwarding-range<T> || View<decay_t<T>>);

24.5 Range utilities [range.utility]

The components in this subclause are general utilities for representing and manipulating ranges.

24.5.1 Helper concepts [range.utility.helpers]

Many of the types in this subclause are specified in terms of the following exposition-only concepts:
template<class R>
  concept simple-view =                         // exposition only
    View<R> && Range<const R> &&
    Same<iterator_t<R>, iterator_t<const R>> &&
    Same<sentinel_t<R>, sentinel_t<const R>>;

template<InputIterator I>
  concept has-arrow =                           // exposition only
    is_pointer_v<I> || requires(I i) { i.operator->(); };

template<class T, class U>
  concept not-same-as =                         // exposition only
    !Same<remove_cvref_t<T>, remove_cvref_t<U>>;

24.5.2 View interface [view.interface]

The class template view_­interface is a helper for defining View-like types that offer a container-like interface.
It is parameterized with the type that is derived from it.
namespace std::ranges {
  template<Range R>
  struct range-common-iterator-impl {                   // exposition only
    using type = common_iterator<iterator_t<R>, sentinel_t<R>>;
  };
  template<CommonRange R>
  struct range-common-iterator-impl<R> {                // exposition only
    using type = iterator_t<R>;
  };
  template<Range R>
    using range-common-iterator =                       // exposition only
      typename range-common-iterator-impl<R>::type;

  template<class D>
    requires is_class_v<D> && Same<D, remove_cv_t<D>>
  class view_interface : public view_base {
  private:
    constexpr D& derived() noexcept {                   // exposition only
      return static_cast<D&>(*this);
    }
    constexpr const D& derived() const noexcept {       // exposition only
      return static_cast<const D&>(*this);
    }
  public:
    constexpr bool empty() requires ForwardRange<D> {
      return ranges::begin(derived()) == ranges::end(derived());
    }
    constexpr bool empty() const requires ForwardRange<const D> {
      return ranges::begin(derived()) == ranges::end(derived());
    }

    constexpr explicit operator bool()
      requires requires { ranges::empty(derived()); } {
        return !ranges::empty(derived());
      }
    constexpr explicit operator bool() const
      requires requires { ranges::empty(derived()); } {
        return !ranges::empty(derived());
      }

    constexpr auto data() requires ContiguousIterator<iterator_t<D>> {
      return ranges::empty(derived()) ? nullptr : addressof(*ranges::begin(derived()));
    }
    constexpr auto data() const
      requires Range<const D> && ContiguousIterator<iterator_t<const D>> {
        return ranges::empty(derived()) ? nullptr : addressof(*ranges::begin(derived()));
      }

    constexpr auto size() requires ForwardRange<D> &&
      SizedSentinel<sentinel_t<D>, iterator_t<D>> {
        return ranges::end(derived()) - ranges::begin(derived());
      }
    constexpr auto size() const requires ForwardRange<const D> &&
      SizedSentinel<sentinel_t<const D>, iterator_t<const D>> {
        return ranges::end(derived()) - ranges::begin(derived());
      }

    constexpr decltype(auto) front() requires ForwardRange<D>;
    constexpr decltype(auto) front() const requires ForwardRange<const D>;

    constexpr decltype(auto) back() requires BidirectionalRange<D> && CommonRange<D>;
    constexpr decltype(auto) back() const
      requires BidirectionalRange<const D> && CommonRange<const D>;

    template<RandomAccessRange R = D>
      constexpr decltype(auto) operator[](iter_difference_t<iterator_t<R>> n) {
        return ranges::begin(derived())[n];
      }
    template<RandomAccessRange R = const D>
      constexpr decltype(auto) operator[](iter_difference_t<iterator_t<R>> n) const {
        return ranges::begin(derived())[n];
      }
  };
}
The template parameter D for view_­interface may be an incomplete type.
Before any member of the resulting specialization of view_­interface other than special member functions is referenced, D shall be complete, and model both DerivedFrom<view_­interface<D>> and View.

24.5.2.1 Members [view.interface.members]

constexpr decltype(auto) front() requires ForwardRange<D>; constexpr decltype(auto) front() const requires ForwardRange<const D>;
Expects: !empty().
Effects: Equivalent to: return *ranges::begin(derived());
constexpr decltype(auto) back() requires BidirectionalRange<D> && CommonRange<D>; constexpr decltype(auto) back() const requires BidirectionalRange<const D> && CommonRange<const D>;
Expects: !empty().
Effects: Equivalent to: return *ranges::prev(ranges::end(derived()));

24.5.3 Sub-ranges [range.subrange]

The subrange class template combines together an iterator and a sentinel into a single object that models the View concept.
Additionally, it models the SizedRange concept when the final template parameter is subrange_­kind::sized.
namespace std::ranges {
  template<class T>
    concept pair-like =                                 // exposition only
      !is_reference_v<T> && requires(T t) {
        typename tuple_size<T>::type;   // ensures tuple_­size<T> is complete
        requires DerivedFrom<tuple_size<T>, integral_constant<size_t, 2>>;
        typename tuple_element_t<0, remove_const_t<T>>;
        typename tuple_element_t<1, remove_const_t<T>>;
        { get<0>(t) } -> const tuple_element_t<0, T>&;
        { get<1>(t) } -> const tuple_element_t<1, T>&;
      };

  template<class T, class U, class V>
    concept pair-like-convertible-to =                  // exposition only
      !Range<T> && pair-like<remove_reference_t<T>> &&
      requires(T&& t) {
        { get<0>(std::forward<T>(t)) } -> ConvertibleTo<U>;
        { get<1>(std::forward<T>(t)) } -> ConvertibleTo<V>;
      };

  template<class T, class U, class V>
    concept pair-like-convertible-from =                // exposition only
      !Range<T> && pair-like<T> && Constructible<T, U, V>;

  template<class T>
    concept iterator-sentinel-pair =                    // exposition only
      !Range<T> && pair-like<T> &&
      Sentinel<tuple_element_t<1, T>, tuple_element_t<0, T>>;

  template<Iterator I, Sentinel<I> S = I, subrange_kind K =
      SizedSentinel<S, I> ? subrange_kind::sized : subrange_kind::unsized>
    requires (K == subrange_kind::sized || !SizedSentinel<S, I>)
  class subrange : public view_interface<subrange<I, S, K>> {
  private:
    static constexpr bool StoreSize =                   // exposition only
      K == subrange_kind::sized && !SizedSentinel<S, I>;
    I begin_ = I();                                     // exposition only
    S end_ = S();                                       // exposition only
    iter_difference_t<I> size_ = 0;                     // exposition only; present only
                                                        // when StoreSize is true
  public:
    subrange() = default;

    constexpr subrange(I i, S s) requires (!StoreSize);

    constexpr subrange(I i, S s, iter_difference_t<I> n)
      requires (K == subrange_kind::sized);

    template<not-same-as<subrange> R>
      requires forwarding-range<R> &&
        ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S>
    constexpr subrange(R&& r) requires (!StoreSize || SizedRange<R>);

    template<forwarding-range R>
      requires ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S>
    constexpr subrange(R&& r, iter_difference_t<I> n)
      requires (K == subrange_kind::sized)
        : subrange{ranges::begin(r), ranges::end(r), n}
    {}

    template<not-same-as<subrange> PairLike>
      requires pair-like-convertible-to<PairLike, I, S>
    constexpr subrange(PairLike&& r) requires (!StoreSize)
      : subrange{std::get<0>(std::forward<PairLike>(r)),
                 std::get<1>(std::forward<PairLike>(r))}
    {}

    template<pair-like-convertible-to<I, S> PairLike>
    constexpr subrange(PairLike&& r, iter_difference_t<I> n)
      requires (K == subrange_kind::sized)
      : subrange{std::get<0>(std::forward<PairLike>(r)),
                 std::get<1>(std::forward<PairLike>(r)), n}
    {}

    template<not-same-as<subrange> PairLike>
      requires pair-like-convertible-from<PairLike, const I&, const S&>
    constexpr operator PairLike() const;

    constexpr I begin() const;
    constexpr S end() const;

    constexpr bool empty() const;
    constexpr iter_difference_t<I> size() const
      requires (K == subrange_kind::sized);

    [[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const;
    [[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const
      requires BidirectionalIterator<I>;
    constexpr subrange& advance(iter_difference_t<I> n);

    friend constexpr I begin(subrange&& r) { return r.begin(); }
    friend constexpr S end(subrange&& r) { return r.end(); }
  };

  template<Iterator I, Sentinel<I> S>
    subrange(I, S, iter_difference_t<I>) -> subrange<I, S, subrange_kind::sized>;

  template<iterator-sentinel-pair P>
    subrange(P) -> subrange<tuple_element_t<0, P>, tuple_element_t<1, P>>;

  template<iterator-sentinel-pair P>
    subrange(P, iter_difference_t<tuple_element_t<0, P>>) ->
      subrange<tuple_element_t<0, P>, tuple_element_t<1, P>, subrange_kind::sized>;

  template<forwarding-range R>
    subrange(R&&) ->
      subrange<iterator_t<R>, sentinel_t<R>,
               (SizedRange<R> || SizedSentinel<sentinel_t<R>, iterator_t<R>>)
                 ? subrange_kind::sized : subrange_kind::unsized>;

  template<forwarding-range R>
    subrange(R&&, iter_difference_t<iterator_t<R>>) ->
      subrange<iterator_t<R>, sentinel_t<R>, subrange_kind::sized>;

  template<size_t N, class I, class S, subrange_kind K>
    requires (N < 2)
  constexpr auto get(const subrange<I, S, K>& r);
}

namespace std {
  using ranges::get;
}

24.5.3.1 Constructors and conversions [range.subrange.ctor]

constexpr subrange(I i, S s) requires (!StoreSize);
Expects: [i, s) is a valid range.
Effects: Initializes begin_­ with i and end_­ with s.
constexpr subrange(I i, S s, iter_difference_t<I> n) requires (K == subrange_kind::sized);
Expects: [i, s) is a valid range, and n == ranges::distance(i, s).
Effects: Initializes begin_­ with i and end_­ with s.
If StoreSize is true, initializes size_­ with n.
[Note
:
Accepting the length of the range and storing it to later return from size() enables subrange to model SizedRange even when it stores an iterator and sentinel that do not model SizedSentinel.
end note
]
template<not-same-as<subrange> R> requires forwarding-range<R> && ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || SizedRange<R>);
Effects: Equivalent to:
  • If StoreSize is true, subrange{ranges::begin(r), ranges::end(r), ranges::size(r)}.
  • Otherwise, subrange{ranges::begin(r), ranges::end(r)}.
template<not-same-as<subrange> PairLike> requires pair-like-convertible-from<PairLike, const I&, const S&> constexpr operator PairLike() const;
Effects: Equivalent to: return PairLike(begin_­, end_­);

24.5.3.2 Accessors [range.subrange.access]

constexpr I begin() const;
Effects: Equivalent to: return begin_­;
constexpr S end() const;
Effects: Equivalent to: return end_­;
constexpr bool empty() const;
Effects: Equivalent to: return begin_­ == end_­;
constexpr iter_difference_t<I> size() const requires (K == subrange_kind::sized);
Effects:
  • If StoreSize is true, equivalent to: return size_­;
  • Otherwise, equivalent to: return end_­ - begin_­;
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const;
Effects: Equivalent to:
auto tmp = *this;
tmp.advance(n);
return tmp;
[Note
:
If I does not model ForwardIterator, next can invalidate *this.
end note
]
[[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const requires BidirectionalIterator<I>;
Effects: Equivalent to:
auto tmp = *this;
tmp.advance(-n);
return tmp;
constexpr subrange& advance(iter_difference_t<I> n);
Effects: Equivalent to:
  • If StoreSize is true,
    size_ -= n - ranges::advance(begin_, n, end_);
    return *this;
    
  • Otherwise,
    ranges::advance(begin_, n, end_);
    return *this;
    
template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(const subrange<I, S, K>& r);
Effects: Equivalent to:
if constexpr (N == 0)
  return r.begin();
else
  return r.end();

24.5.4 Dangling iterator handling [range.dangling]

The tag type dangling is used together with the template aliases safe_­iterator_­t and safe_­subrange_­t to indicate that an algorithm that typically returns an iterator into or subrange of a Range argument does not return an iterator or subrange which could potentially reference a range whose lifetime has ended for a particular rvalue Range argument which does not model forwarding-range ([range.range]).
namespace std {
  struct dangling {
    constexpr dangling() noexcept = default;
    template<class... Args>
      constexpr dangling(Args&&...) noexcept { }
  };
}
[Example
:
vector<int> f();
auto result1 = ranges::find(f(), 42);                                   // #1
static_assert(Same<decltype(result1), dangling>);
auto vec = f();
auto result2 = ranges::find(vec, 42);                                   // #2
static_assert(Same<decltype(result2), vector<int>::iterator>);
auto result3 = ranges::find(subrange{vec}, 42);                         // #3
static_assert(Same<decltype(result3), vector<int>::iterator>);
The call to ranges::find at #1 returns dangling since f() is an rvalue vector; the vector could potentially be destroyed before a returned iterator is dereferenced.
However, the calls at #2 and #3 both return iterators since the lvalue vec and specializations of subrange model forwarding-range.
end example
]

24.6 Range factories [range.factories]

This subclause defines range factories, which are utilities to create a View.
Range factories are declared in namespace std::ranges::view.

24.6.1 Empty view [range.empty]

24.6.1.1 Overview [range.empty.overview]

empty_­view produces a View of no elements of a particular type.
[Example
:
empty_view<int> e;
static_assert(ranges::empty(e));
static_assert(0 == e.size());
end example
]

24.6.1.2 Class template empty_­view [range.empty.view]

namespace std::ranges {
  template<class T>
    requires is_object_v<T>
  class empty_view : public view_interface<empty_view<T>> {
  public:
    static constexpr T* begin() noexcept { return nullptr; }
    static constexpr T* end() noexcept { return nullptr; }
    static constexpr T* data() noexcept { return nullptr; }
    static constexpr ptrdiff_t size() noexcept { return 0; }
    static constexpr bool empty() noexcept { return true; }

    friend constexpr T* begin(empty_view) noexcept { return nullptr; }
    friend constexpr T* end(empty_view) noexcept { return nullptr; }
  };
}

24.6.2 Single view [range.single]

24.6.2.1 Overview [range.single.overview]

single_­view produces a View that contains exactly one element of a specified value.
[Example
:
single_view s{4};
for (int i : s)
  cout << i; // prints 4
end example
]

24.6.2.2 Class template single_­view [range.single.view]

namespace std::ranges {
  template<CopyConstructible T>
    requires is_object_v<T>
  class single_view : public view_interface<single_view<T>> {
  private:
    semiregular<T> value_;      // exposition only
  public:
    single_view() = default;
    constexpr explicit single_view(const T& t);
    constexpr explicit single_view(T&& t);
    template<class... Args>
      requires Constructible<T, Args...>
    constexpr single_view(in_place_t, Args&&... args);

    constexpr T* begin() noexcept;
    constexpr const T* begin() const noexcept;
    constexpr T* end() noexcept;
    constexpr const T* end() const noexcept;
    static constexpr ptrdiff_t size() noexcept;
    constexpr T* data() noexcept;
    constexpr const T* data() const noexcept;
  };
}
constexpr explicit single_view(const T& t);
Effects: Initializes value_­ with t.
constexpr explicit single_view(T&& t);
Effects: Initializes value_­ with std::move(t).
template<class... Args> constexpr single_view(in_place_t, Args&&... args);
Effects: Initializes value_­ as if by value_­{in_­place, std::forward<Args>(args)...}.
constexpr T* begin() noexcept; constexpr const T* begin() const noexcept;
Effects: Equivalent to: return data();
constexpr T* end() noexcept; constexpr const T* end() const noexcept;
Effects: Equivalent to: return data() + 1;
static constexpr ptrdiff_t size() noexcept;
Effects: Equivalent to: return 1;
constexpr T* data() noexcept; constexpr const T* data() const noexcept;
Effects: Equivalent to: return value_­.operator->();

24.6.2.3 view​::​single [range.single.adaptor]

The name view::single denotes a customization point object ([customization.point.object]).
For some subexpression E, the expression view::single(E) is expression-equivalent to single_­view{E}.

24.6.3 Iota view [range.iota]

24.6.3.1 Overview [range.iota.overview]

iota_­view generates a sequence of elements by repeatedly incrementing an initial value.
[Example
:
for (int i : iota_view{1, 10})
  cout << i << ' '; // prints: 1 2 3 4 5 6 7 8 9
end example
]

24.6.3.2 Class template iota_­view [range.iota.view]

namespace std::ranges {
  template<class I>
    concept Decrementable =     // exposition only
      see below;
  template<class I>
    concept Advanceable =       // exposition only
      see below;

  template<WeaklyIncrementable W, Semiregular Bound = unreachable_sentinel_t>
    requires weakly-equality-comparable-with<W, Bound>
  class iota_view : public view_interface<iota_view<W, Bound>> {
  private:
    // [range.iota.iterator], class iota_­view​::​iterator
    struct iterator;            // exposition only
    // [range.iota.sentinel], class iota_­view​::​sentinel
    struct sentinel;            // exposition only
    W value_ = W();             // exposition only
    Bound bound_ = Bound();     // exposition only
  public:
    iota_view() = default;
    constexpr explicit iota_view(W value);
    constexpr iota_view(type_identity_t<W> value,
                        type_identity_t<Bound> bound);

    constexpr iterator begin() const;
    constexpr sentinel end() const;
    constexpr iterator end() const requires Same<W, Bound>;

    constexpr auto size() const
      requires (Same<W, Bound> && Advanceable<W>) ||
               (Integral<W> && Integral<Bound>) ||
               SizedSentinel<Bound, W>
    { return bound_ - value_; }
  };

  template<class W, class Bound>
    requires (!Integral<W> || !Integral<Bound> || is_signed_v<W> == is_signed_v<Bound>)
  iota_view(W, Bound) -> iota_view<W, Bound>;
}
The exposition-only Decrementable concept is equivalent to:
template<class I> concept Decrementable = Incrementable<I> && requires(I i) { { --i } -> Same<I&>; { i-- } -> Same<I>; };
When an object is in the domain of both pre- and post-decrement, the object is said to be decrementable.
Let a and b be equal objects of type I.
I models Decrementable only if
  • If a and b are decrementable, then the following are all true:
  • If a and b are incrementable, then bool(--(++a) == b).
The exposition-only Advanceable concept is equivalent to:
template<class I> concept Advanceable = Decrementable<I> && StrictTotallyOrdered<I> && requires(I i, const I j, const iter_difference_t<I> n) { { i += n } -> Same<I&>; { i -= n } -> Same<I&>; { j + n } -> Same<I>; { n + j } -> Same<I>; { j - n } -> Same<I>; { j - j } -> Same<iter_difference_t<I>>; };
Let a and b be objects of type I such that b is reachable from a after n applications of ++a, for some value n of type iter_­difference_­t<I>, and let D be iter_­difference_­t<I>.
I models Advanceable only if
  • (a += n) is equal to b.
  • addressof(a += n) is equal to addressof(a).
  • (a + n) is equal to (a += n).
  • For any two positive values x and y of type D, if (a + D(x + y)) is well-defined, then (a + D(x + y)) is equal to ((a + x) + y).
  • (a + D(0)) is equal to a.
  • If (a + D(n - 1)) is well-defined, then (a + n) is equal to ++(a + D(n - 1)).
  • (b += -n) is equal to a.
  • (b -= n) is equal to a.
  • addressof(b -= n) is equal to addressof(b).
  • (b - n) is equal to (b -= n).
  • (b - a) is equal to n.
  • (a - b) is equal to -n.
  • bool(a <= b) is true.
constexpr explicit iota_view(W value);
Expects: Bound denotes unreachable_­sentinel_­t or Bound() is reachable from value.
Effects: Initializes value_­ with value.
constexpr iota_view(type_identity_t<W> value, type_identity_t<Bound> bound);
Expects: Bound denotes unreachable_­sentinel_­t or bound is reachable from value.
Effects: Initializes value_­ with value and bound_­ with bound.
constexpr iterator begin() const;
Effects: Equivalent to: return iterator{value_­};
constexpr sentinel end() const;
Effects: Equivalent to: return sentinel{bound_­};
constexpr iterator end() const requires Same<W, Bound>;
Effects: Equivalent to: return iterator{bound_­};

24.6.3.3 Class iota_­view​::​iterator [range.iota.iterator]

namespace std::ranges {
  template<class W, class Bound>
  struct iota_view<W, Bound>::iterator {
  private:
    W value_ = W();             // exposition only
  public:
    using iterator_category = see below;
    using value_type = W;
    using difference_type = iter_difference_t<W>;

    iterator() = default;
    constexpr explicit iterator(W value);

    constexpr W operator*() const noexcept(is_nothrow_copy_constructible_v<W>);

    constexpr iterator& operator++();
    constexpr void operator++(int);
    constexpr iterator operator++(int) requires Incrementable<W>;

    constexpr iterator& operator--() requires Decrementable<W>;
    constexpr iterator operator--(int) requires Decrementable<W>;

    constexpr iterator& operator+=(difference_type n)
      requires Advanceable<W>;
    constexpr iterator& operator-=(difference_type n)
      requires Advanceable<W>;
    constexpr W operator[](difference_type n) const
      requires Advanceable<W>;

    friend constexpr bool operator==(const iterator& x, const iterator& y)
      requires EqualityComparable<W>;
    friend constexpr bool operator!=(const iterator& x, const iterator& y)
      requires EqualityComparable<W>;

    friend constexpr bool operator<(const iterator& x, const iterator& y)
      requires StrictTotallyOrdered<W>;
    friend constexpr bool operator>(const iterator& x, const iterator& y)
      requires StrictTotallyOrdered<W>;
    friend constexpr bool operator<=(const iterator& x, const iterator& y)
      requires StrictTotallyOrdered<W>;
    friend constexpr bool operator>=(const iterator& x, const iterator& y)
      requires StrictTotallyOrdered<W>;

    friend constexpr iterator operator+(iterator i, difference_type n)
      requires Advanceable<W>;
    friend constexpr iterator operator+(difference_type n, iterator i)
      requires Advanceable<W>;

    friend constexpr iterator operator-(iterator i, difference_type n)
      requires Advanceable<W>;
    friend constexpr difference_type operator-(const iterator& x, const iterator& y)
      requires Advanceable<W>;
  };
}
iterator::iterator_­category is defined as follows:
  • If W models Advanceable, then iterator_­category is random_­access_­iterator_­tag.
  • Otherwise, if W models Decrementable, then iterator_­category is bidirectional_­iterator_­tag.
  • Otherwise, if W models Incrementable, then iterator_­category is forward_­iterator_­tag.
  • Otherwise, iterator_­category is input_­iterator_­tag.
[Note
:
Overloads for iter_­move and iter_­swap are omitted intentionally.
end note
]
constexpr explicit iterator(W value);
Effects: Initializes value_­ with value.
constexpr W operator*() const noexcept(is_nothrow_copy_constructible_v<W>);
Effects: Equivalent to: return value_­;
[Note
:
The noexcept clause is needed by the default iter_­move implementation.
end note
]
constexpr iterator& operator++();
Effects: Equivalent to:
++value_;
return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
constexpr iterator operator++(int) requires Incrementable<W>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires Decrementable<W>;
Effects: Equivalent to:
--value_;
return *this;
constexpr iterator operator--(int) requires Decrementable<W>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
constexpr iterator& operator+=(difference_type n) requires Advanceable<W>;
Effects: Equivalent to:
value_ += n;
return *this;
constexpr iterator& operator-=(difference_type n) requires Advanceable<W>;
Effects: Equivalent to:
value_ -= n;
return *this;
constexpr W operator[](difference_type n) const requires Advanceable<W>;
Effects: Equivalent to: return value_­ + n;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires EqualityComparable<W>;
Effects: Equivalent to: return x.value_­ == y.value_­;
friend constexpr bool operator!=(const iterator& x, const iterator& y) requires EqualityComparable<W>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator<(const iterator& x, const iterator& y) requires StrictTotallyOrdered<W>;
Effects: Equivalent to: return x.value_­ < y.value_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires StrictTotallyOrdered<W>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires StrictTotallyOrdered<W>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires StrictTotallyOrdered<W>;
Effects: Equivalent to: return !(x < y);
friend constexpr iterator operator+(iterator i, difference_type n) requires Advanceable<W>;
Effects: Equivalent to: return iterator{i.value_­ + n};
friend constexpr iterator operator+(difference_type n, iterator i) requires Advanceable<W>;
Effects: Equivalent to: return i + n;
friend constexpr iterator operator-(iterator i, difference_type n) requires Advanceable<W>;
Effects: Equivalent to: return i + -n;
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires Advanceable<W>;
Effects: Equivalent to: return x.value_­ - y.value_­;

24.6.3.4 Class iota_­view​::​sentinel [range.iota.sentinel]

namespace std::ranges {
  template<class W, class Bound>
  struct iota_view<W, Bound>::sentinel {
  private:
    Bound bound_ = Bound();     // exposition only
  public:
    sentinel() = default;
    constexpr explicit sentinel(Bound bound);

    friend constexpr bool operator==(const iterator& x, const sentinel& y);
    friend constexpr bool operator==(const sentinel& x, const iterator& y);
    friend constexpr bool operator!=(const iterator& x, const sentinel& y);
    friend constexpr bool operator!=(const sentinel& x, const iterator& y);
  };
}
constexpr explicit sentinel(Bound bound);
Effects: Initializes bound_­ with bound.
friend constexpr bool operator==(const iterator& x, const sentinel& y);
Effects: Equivalent to: return x.value_­ == y.bound_­;
friend constexpr bool operator==(const sentinel& x, const iterator& y);
Effects: Equivalent to: return y == x;
friend constexpr bool operator!=(const iterator& x, const sentinel& y);
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator!=(const sentinel& x, const iterator& y);
Effects: Equivalent to: return !(y == x);

24.6.3.5 view​::​iota [range.iota.adaptor]

The name view::iota denotes a customization point object ([customization.point.object]).
For some subexpressions E and F, the expressions view::iota(E) and view::iota(E, F) are expression-equivalent to iota_­view{E} and iota_­view{E, F}, respectively.

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::view.
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 | view::filter(even) | view::transform(square)) {
  cout << i << ' '; // prints: 0 4 16
}
assert(ranges::equal(ints | view::filter(even), view::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 ViewableRange argument and returns a View.
For a range adaptor closure object C and an expression R such that decltype((R)) models ViewableRange, 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 ViewableRange 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 of the types in this subclause are specified in terms of an exposition-only class template semiregular.
semiregular<T> behaves exactly like optional<T> with the following differences:
  • semiregular<T> constrains its type parameter T with CopyConstructible<T> && is_­object_­v<T>.
  • If T models DefaultConstructible, the default constructor of semiregular<T> is equivalent to:
    constexpr semiregular() noexcept(is_nothrow_default_constructible_v<T>)
      : semiregular{in_place}
    { }
    
  • If Assignable<T&, const T&> is not satisfied, the copy assignment operator is equivalent to:
    semiregular& operator=(const semiregular& that)
      noexcept(is_nothrow_copy_constructible_v<T>)
    {
      if (that) emplace(*that);
      else reset();
      return *this;
    }
    
  • If Assignable<T&, T> is not satisfied, the move assignment operator is equivalent to:
    semiregular& operator=(semiregular&& 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]

view::all returns a View that includes all elements of its Range argument.
The name view::all denotes a range adaptor object ([range.adaptor.object]).
For some subexpression E, the expression view::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 SizedRange<R>
    { return ranges::size(*r_); }

    constexpr auto data() const requires ContiguousRange<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
ConvertibleTo<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<InputRange V, IndirectUnaryPredicate<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<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<InputRange R>
      requires ViewableRange<R> && Constructible<V, all_view<R>>
    constexpr filter_view(R&& r, Pred pred);

    constexpr V base() const;

    constexpr iterator begin();
    constexpr auto end() {
      if constexpr (CommonRange<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<InputRange R> requires ViewableRange<R> && Constructible<V, all_view<R>> constexpr filter_view(R&& r, Pred pred);
Effects: Initializes base_­ with view::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        = iter_value_t<iterator_t<V>>;
    using difference_type   = iter_difference_t<iterator_t<V>>;

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

    constexpr iterator_t<V> base() const;
    constexpr iter_reference_t<iterator_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 ForwardRange<V>;

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

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

    friend constexpr iter_rvalue_reference_t<iterator_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 IndirectlySwappable<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 BidirectionalRange, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if V models ForwardRange, 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 DerivedFrom<bidirectional_­iterator_­tag>, then iterator_­category denotes bidirectional_­iterator_­tag.
  • Otherwise, if C models DerivedFrom<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 iter_reference_t<iterator_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 ForwardRange<V>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires BidirectionalRange<V>;
Effects: Equivalent to:
do
  --current_;
while (!invoke(*parent_->pred_, *current_));
return *this;
constexpr iterator operator--(int) requires BidirectionalRange<V>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires EqualityComparable<iterator_t<V>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator!=(const iterator& x, const iterator& y) requires EqualityComparable<iterator_t<V>>;
Effects: Equivalent to: return !(x == y);
friend constexpr iter_rvalue_reference_t<iterator_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 IndirectlySwappable<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);
    friend constexpr bool operator==(const sentinel& x, const iterator& y);
    friend constexpr bool operator!=(const iterator& x, const sentinel& y);
    friend constexpr bool operator!=(const sentinel& x, const iterator& y);
  };
}
constexpr explicit sentinel(filter_view& parent);
Effects: Initializes end_­ with ranges::end(parent).
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_­;
friend constexpr bool operator==(const sentinel& x, const iterator& y);
Effects: Equivalent to: return y == x;
friend constexpr bool operator!=(const iterator& x, const sentinel& y);
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator!=(const sentinel& x, const iterator& y);
Effects: Equivalent to: return !(y == x);

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

The name view::filter denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and P, the expression view::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<InputRange V, CopyConstructible F>
    requires View<V> && is_object_v<F> &&
             RegularInvocable<F&, iter_reference_t<iterator_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<F> fun_;                        // exposition only

  public:
    transform_view() = default;
    constexpr transform_view(V base, F fun);
    template<InputRange R>
      requires ViewableRange<R> && Constructible<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> &&
               RegularInvocable<const F&, iter_reference_t<iterator_t<const V>>>;

    constexpr sentinel<false> end();
    constexpr iterator<false> end() requires CommonRange<V>;
    constexpr sentinel<true> end() const
      requires Range<const V> &&
               RegularInvocable<const F&, iter_reference_t<iterator_t<const V>>>;
    constexpr iterator<true> end() const
      requires CommonRange<const V> &&
               RegularInvocable<const F&, iter_reference_t<iterator_t<const V>>>;

    constexpr auto size() requires SizedRange<V> { return ranges::size(base_); }
    constexpr auto size() const requires SizedRange<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<InputRange R> requires ViewableRange<R> && Constructible<V, all_view<R>> constexpr transform_view(R&& r, F fun);
Effects: Initializes base_­ with view::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> && RegularInvocable<const F&, iter_reference_t<iterator_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 CommonRange<V>;
Effects: Equivalent to:
return iterator<false>{*this, ranges::end(base_)};
constexpr sentinel<true> end() const requires Range<const V> && RegularInvocable<const F&, iter_reference_t<iterator_t<const V>>>;
Effects: Equivalent to:
return sentinel<true>{ranges::end(base_)};
constexpr iterator<true> end() const requires CommonRange<const V> && RegularInvocable<const F&, iter_reference_t<iterator_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&, iter_reference_t<iterator_t<Base>>>>;
    using difference_type   = iter_difference_t<iterator_t<Base>>;

    iterator() = default;
    constexpr iterator(Parent& parent, iterator_t<Base> current);
    constexpr iterator(iterator<!Const> i)
      requires Const && ConvertibleTo<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 ForwardRange<Base>;

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

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

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

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

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

    friend constexpr iterator operator-(iterator i, difference_type n)
      requires RandomAccessRange<Base>;
    friend constexpr difference_type operator-(const iterator& x, const iterator& y)
      requires RandomAccessRange<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 IndirectlySwappable<iterator_t<Base>>;
  };
}
iterator::iterator_­concept is defined as follows:
  • If V models RandomAccessRange, then iterator_­concept denotes random_­access_­iterator_­tag.
  • Otherwise, if V models BidirectionalRange, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if V models ForwardRange, 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 DerivedFrom<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 && ConvertibleTo<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 ForwardRange<Base>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires BidirectionalRange<Base>;
Effects: Equivalent to:
--current_;
return *this;
constexpr iterator operator--(int) requires BidirectionalRange<Base>;
Effects: Equivalent to:
auto tmp = *this;
--*this;
return tmp;
constexpr iterator& operator+=(difference_type n) requires RandomAccessRange<Base>;
Effects: Equivalent to:
current_ += n;
return *this;
constexpr iterator& operator-=(difference_type n) requires RandomAccessRange<Base>;
Effects: Equivalent to:
current_ -= n;
return *this;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires EqualityComparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator!=(const iterator& x, const iterator& y) requires EqualityComparable<iterator_t<Base>>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator<(const iterator& x, const iterator& y) requires RandomAccessRange<Base>;
Effects: Equivalent to: return x.current_­ < y.current_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires RandomAccessRange<Base>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires RandomAccessRange<Base>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires RandomAccessRange<Base>;
Effects: Equivalent to: return !(x < y);
friend constexpr iterator operator+(iterator i, difference_type n) requires RandomAccessRange<Base>; friend constexpr iterator operator+(difference_type n, iterator i) requires RandomAccessRange<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ + n};
friend constexpr iterator operator-(iterator i, difference_type n) requires RandomAccessRange<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ - n};
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires RandomAccessRange<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 IndirectlySwappable<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 && ConvertibleTo<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 bool operator==(const sentinel& x, const iterator<Const>& y);
    friend constexpr bool operator!=(const iterator<Const>& x, const sentinel& y);
    friend constexpr bool operator!=(const sentinel& x, const iterator<Const>& y);

    friend constexpr iter_difference_t<iterator_t<Base>>
      operator-(const iterator<Const>& x, const sentinel& y)
        requires SizedSentinel<sentinel_t<Base>, iterator_t<Base>>;
    friend constexpr iter_difference_t<iterator_t<Base>>
      operator-(const sentinel& y, const iterator<Const>& x)
        requires SizedSentinel<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 && ConvertibleTo<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 bool operator==(const sentinel& x, const iterator<Const>& y);
Effects: Equivalent to: return y == x;
friend constexpr bool operator!=(const iterator<Const>& x, const sentinel& y);
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator!=(const sentinel& x, const iterator<Const>& y);
Effects: Equivalent to: return !(y == x);
friend constexpr iter_difference_t<iterator_t<Base>> operator-(const iterator<Const>& x, const sentinel& y) requires SizedSentinel<sentinel_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ - y.end_­;
friend constexpr iter_difference_t<iterator_t<Base>> operator-(const sentinel& y, const iterator<Const>& x) requires SizedSentinel<sentinel_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.end_­ - y.current_­;

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

The name view::transform denotes a range adaptor object ([range.adaptor.object]).
For some subexpressions E and F, the expression view::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
    iter_difference_t<iterator_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, iter_difference_t<iterator_t<V>> count);
    template<ViewableRange R>
      requires Constructible<V, all_view<R>>
    constexpr take_view(R&& r, iter_difference_t<iterator_t<V>> count);

    constexpr V base() const;

    constexpr auto begin() requires (!simple-view<V>) {
      if constexpr (SizedRange<V>) {
        if constexpr (RandomAccessRange<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 (SizedRange<const V>) {
        if constexpr (RandomAccessRange<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 (SizedRange<V>) {
        if constexpr (RandomAccessRange<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 (SizedRange<const V>) {
        if constexpr (RandomAccessRange<const V>)
          return ranges::begin(base_) + size();
        else
          return default_sentinel;
      } else
        return sentinel<true>{ranges::end(base_)};
    }

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

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

  template<Range R>
    take_view(R&&, iter_difference_t<iterator_t<R>>)
      -> take_view<all_view<R>>;
}
constexpr take_view(V base, iter_difference_t<iterator_t<V>> count);
Effects: Initializes base_­ with std::move(base) and count_­ with count.
template<ViewableRange R> requires Constructible<V, all_view<R>> constexpr take_view(R&& r, iter_difference_t<iterator_t<V>> count);
Effects: Initializes base_­ with view::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 && ConvertibleTo<sentinel_t<V>, sentinel_t<Base>>;

    constexpr sentinel_t<Base> base() const;

    friend constexpr bool operator==(const sentinel& x, const CI& y);
    friend constexpr bool operator==(const CI& y, const sentinel& x);
    friend constexpr bool operator!=(const sentinel& x, const CI& y);
    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 && ConvertibleTo<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 sentinel& x, const CI& y); friend constexpr bool operator==(const CI& y, const sentinel& x);
Effects: Equivalent to: return y.count() == 0 || y.base() == x.end_­;
friend constexpr bool operator!=(const sentinel& x, const CI& y); friend constexpr bool operator!=(const CI& y, const sentinel& x);
Effects: Equivalent to: return !(x == y);

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

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

24.7.7 Join view [range.join]

24.7.7.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.7.2 Class template join_­view [range.join.view]

namespace std::ranges {
  template<InputRange V>
    requires View<V> && InputRange<iter_reference_t<iterator_t<V>>> &&
             (is_reference_v<iter_reference_t<iterator_t<V>>> ||
              View<iter_value_t<iterator_t<V>>>)
  class join_view : public view_interface<join_view<V>> {
  private:
    using InnerRng =                    // exposition only
      iter_reference_t<iterator_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<InputRange R>
      requires ViewableRange<R> && Constructible<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 InputRange<const V> &&
             is_reference_v<iter_reference_t<iterator_t<const V>>> {
      return iterator<true>{*this, ranges::begin(base_)};
    }

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

    constexpr auto end() const
    requires InputRange<const V> &&
             is_reference_v<iter_reference_t<iterator_t<const V>>> {
      if constexpr (ForwardRange<const V> &&
                    is_reference_v<iter_reference_t<iterator_t<const V>>> &&
                    ForwardRange<iter_reference_t<iterator_t<const V>>> &&
                    CommonRange<const V> &&
                    CommonRange<iter_reference_t<iterator_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<InputRange R> requires ViewableRange<R> && Constructible<V, all_view<R>> constexpr explicit join_view(R&& r);
Effects: Initializes base_­ with view::all(std::forward<R>(r)).

24.7.7.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<iter_reference_t<iterator_t<Base>>>;

    iterator_t<Base> outer_ = iterator_t<Base>();               // exposition only
    iterator_t<iter_reference_t<iterator_t<Base>>> inner_ =     // exposition only
      iterator_t<iter_reference_t<iterator_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        =
      iter_value_t<iterator_t<iter_reference_t<iterator_t<Base>>>>;
    using difference_type   = see below;

    iterator() = default;
    constexpr iterator(Parent& parent, iterator_t<V> outer);
    constexpr iterator(iterator<!Const> i)
      requires Const &&
               ConvertibleTo<iterator_t<V>, iterator_t<Base>> &&
               ConvertibleTo<iterator_t<InnerRng>,
                             iterator_t<iter_reference_t<iterator_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 && ForwardRange<Base> &&
               ForwardRange<iter_reference_t<iterator_t<Base>>>;

    constexpr iterator& operator--()
      requires ref_is_glvalue && BidirectionalRange<Base> &&
               BidirectionalRange<iter_reference_t<iterator_t<Base>>>;

    constexpr iterator operator--(int)
      requires ref_is_glvalue && BidirectionalRange<Base> &&
               BidirectionalRange<iter_reference_t<iterator_t<Base>>>;

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

    friend constexpr bool operator!=(const iterator& x, const iterator& y)
      requires ref_is_glvalue && EqualityComparable<iterator_t<Base>> &&
               EqualityComparable<iterator_t<iter_reference_t<iterator_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 iter_­reference_­t<iterator_­t<Base>> each model BidirectionalRange, then iterator_­concept denotes bidirectional_­iterator_­tag.
    • Otherwise, if Base and iter_­reference_­t<iterator_­t<Base>> each model ForwardRange, 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<iter_­reference_­t<iterator_­t<Base>>>>::iterator_­category.
  • If ref_­is_­glvalue is true,
    • If OUTERC and INNERC each model DerivedFrom<bidirectional_­iterator_­tag>, iterator_­category denotes bidirectional_­iterator_­tag.
    • Otherwise, if OUTERC and INNERC each model DerivedFrom<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<
  iter_difference_t<iterator_t<Base>>,
  iter_difference_t<iterator_t<iter_reference_t<iterator_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](iter_reference_t<iterator_t<Base>> x) -> decltype(auto) {
  if constexpr (ref_is_glvalue) // x is a reference
    return (x);                 // (x) is an lvalue
  else
    return (parent_->inner_ = view::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<iter_reference_t<iterator_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 && ConvertibleTo<iterator_t<V>, iterator_t<Base>> && ConvertibleTo<iterator_t<InnerRng>, iterator_t<iter_reference_t<iterator_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 && ForwardRange<Base> && ForwardRange<iter_reference_t<iterator_t<Base>>>;
Effects: Equivalent to:
auto tmp = *this;
++*this;
return tmp;
constexpr iterator& operator--() requires ref_is_glvalue && BidirectionalRange<Base> && BidirectionalRange<iter_reference_t<iterator_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 && BidirectionalRange<Base> && BidirectionalRange<iter_reference_t<iterator_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 && EqualityComparable<iterator_t<Base>> && EqualityComparable<iterator_t<iter_reference_t<iterator_t<Base>>>>;
Effects: Equivalent to: return x.outer_­ == y.outer_­ && x.inner_­ == y.inner_­;
friend constexpr bool operator!=(const iterator& x, const iterator& y) requires ref_is_glvalue && EqualityComparable<iterator_t<Base>> && EqualityComparable<iterator_t<iter_reference_t<iterator_t<Base>>>>;
Effects: Equivalent to: return !(x == y);
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.7.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 && ConvertibleTo<sentinel_t<V>, sentinel_t<Base>>;

    friend constexpr bool operator==(const iterator<Const>& x, const sentinel& y);
    friend constexpr bool operator==(const sentinel& x, const iterator<Const>& y);
    friend constexpr bool operator!=(const iterator<Const>& x, const sentinel& y);
    friend constexpr bool operator!=(const sentinel& x, const iterator<Const>& y);
  };
}
constexpr explicit sentinel(Parent& parent);
Effects: Initializes end_­ with ranges::end(parent.base_­).
constexpr sentinel(sentinel<!Const> s) requires Const && ConvertibleTo<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_­;
friend constexpr bool operator==(const sentinel& x, const iterator<Const>& y);
Effects: Equivalent to: return y == x;
friend constexpr bool operator!=(const iterator<Const>& x, const sentinel& y);
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator!=(const sentinel& x, const iterator<Const>& y);
Effects: Equivalent to: return !(y == x);

24.7.7.5 view​::​join [range.join.adaptor]

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

24.7.8 Split view [range.split]

24.7.8.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.8.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
    SizedRange<R> &&
    requires { typename require-constant<remove_reference_t<R>::size()>; } &&
    (remove_reference_t<R>::size() <= 1);

  template<InputRange V, ForwardRange Pattern>
    requires View<V> && View<Pattern> &&
             IndirectlyComparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (ForwardRange<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 !ForwardRange<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<InputRange R, ForwardRange P>
      requires Constructible<V, all_view<R>> &&
               Constructible<Pattern, all_view<P>>
    constexpr split_view(R&& r, P&& p);

    template<InputRange R>
      requires Constructible<V, all_view<R>> &&
               Constructible<Pattern, single_view<iter_value_t<iterator_t<R>>>>
    constexpr split_view(R&& r, iter_value_t<iterator_t<R>> e);

    constexpr auto begin() {
      if constexpr (ForwardRange<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 ForwardRange<V> && ForwardRange<const V> {
      return outer_iterator<true>{*this, ranges::begin(base_)};
    }

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

    constexpr auto end() const {
      if constexpr (ForwardRange<V> && ForwardRange<const V> && CommonRange<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<InputRange R>
    split_view(R&&, iter_value_t<iterator_t<R>>)
      -> split_view<all_view<R>, single_view<iter_value_t<iterator_t<R>>>>;
}
constexpr split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std::move(base), and pattern_­ with std::move(pattern).
template<InputRange R, ForwardRange P> requires Constructible<V, all_view<R>> && Constructible<Pattern, all_view<P>> constexpr split_view(R&& r, P&& p);
Effects: Initializes base_­ with view::all(std::forward<R>(r)) and pattern_­ with view::all(std​::forward<P>(p)).
template<InputRange R> requires Constructible<V, all_view<R>> && Constructible<Pattern, single_view<iter_value_t<iterator_t<R>>>> constexpr split_view(R&& r, iter_value_t<iterator_t<R>> e);
Effects: Initializes base_­ with view::all(std::forward<R>(r)) and pattern_­ with single_­view{​std::move(e)}.

24.7.8.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 ForwardRange
      iterator_t<Base>();

  public:
    using iterator_concept  =
      conditional_t<ForwardRange<Base>, forward_iterator_tag, input_iterator_tag>;
    using iterator_category = input_iterator_tag;
    struct value_type;                                  // see [range.split.outer.value]
    using difference_type   = iter_difference_t<iterator_t<Base>>;

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

    constexpr value_type operator*() const;

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

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

    friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
    friend constexpr bool operator==(default_sentinel_t, const outer_iterator& x);
    friend constexpr bool operator!=(const outer_iterator& x, default_sentinel_t y);
    friend constexpr bool operator!=(default_sentinel_t y, const outer_iterator& x);
  };
}
Many of the following specifications refer to the notional member current of outer_­iterator.
current is equivalent to current_­ if V models ForwardRange, and parent_­->current_­ otherwise.
constexpr explicit outer_iterator(Parent& parent) requires (!ForwardRange<Base>);
Effects: Initializes parent_­ with addressof(parent).
constexpr outer_iterator(Parent& parent, iterator_t<Base> current) requires ForwardRange<Base>;
Effects: Initializes parent_­ with addressof(parent) and current_­ with current.
constexpr outer_iterator(outer_iterator<!Const> i) requires Const && ConvertibleTo<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 ForwardRange<Base>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator!=(const outer_iterator& x, const outer_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t); friend constexpr bool operator==(default_sentinel_t, const outer_iterator& x);
Effects: Equivalent to: return x.current == ranges::end(x.parent_­->base_­);
friend constexpr bool operator!=(const outer_iterator& x, default_sentinel_t y); friend constexpr bool operator!=(default_sentinel_t y, const outer_iterator& x);
Effects: Equivalent to: return !(x == y);

24.7.8.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.8.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        = iter_value_t<iterator_t<Base>>;
    using difference_type   = iter_difference_t<iterator_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 (ForwardRange<V>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

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

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

    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 IndirectlySwappable<iterator_t<Base>>;
  };
}
The typedef-name iterator_­category denotes forward_­iterator_­tag if iterator_­traits<iterator_­t<Base>>::iterator_­category models DerivedFrom<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 (!ForwardRange<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 ForwardRange<Base>;
Effects: Equivalent to: return x.i_­.current_­ == y.i_­.current_­;
friend constexpr bool operator!=(const inner_iterator& x, const inner_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t); friend constexpr bool operator==(default_sentinel_t, const inner_iterator& x);
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 bool operator!=(const inner_iterator& x, default_sentinel_t y); friend constexpr bool operator!=(default_sentinel_t y, const inner_iterator& x);
Effects: Equivalent to: return !(x == y);
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 IndirectlySwappable<iterator_t<Base>>;
Effects: Equivalent to ranges::iter_­swap(x.i_­.current, y.i_­.current).

24.7.8.6 view​::​split [range.split.adaptor]

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

24.7.9 Counted view [range.counted]

The name view::counted denotes a customization point object.
Let E and F be expressions, and let T be decay_­t<decltype((E))>.
Then the expression view::counted(E, F) is expression-equivalent to:
  • If T models Iterator and decltype((F)) models ConvertibleTo<iter_­difference_­t<T>>,
    • subrange{E, E + static_­cast<iter_­difference_­t<T>>(F)} if T models RandomAccessIterator.
    • Otherwise, subrange{counted_­iterator{E, F}, default_­sentinel}.
  • Otherwise, view::counted(E, F) is ill-formed.
    [Note
    :
    This case can result in substitution failure when view::counted(E, F) appears in the immediate context of a template instantiation.
    end note
    ]

24.7.10 Common view [range.common]

24.7.10.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<ForwardRange R>
void my_algo(R&& r) {
  auto&& common = common_view{r};
  auto cnt = count(common.begin(), common.end());
  // ...
}
end example
]

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

namespace std::ranges {
  template<View V>
    requires (!CommonRange<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<ViewableRange R>
      requires (!CommonRange<R> && Constructible<V, all_view<R>>)
    constexpr explicit common_view(R&& r);

    constexpr V base() const;

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

    constexpr auto begin() {
      if constexpr (RandomAccessRange<V> && SizedRange<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 (RandomAccessRange<const V> && SizedRange<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 (RandomAccessRange<V> && SizedRange<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 (RandomAccessRange<const V> && SizedRange<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<ViewableRange R> requires (!CommonRange<R> && Constructible<V, all_view<R>>) constexpr explicit common_view(R&& r);
Effects: Initializes base_­ with view::all(std::forward<R>(r)).
constexpr V base() const;
Effects: Equivalent to: return base_­;

24.7.10.3 view​::​common [range.common.adaptor]

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

24.7.11 Reverse view [range.reverse]

24.7.11.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.11.2 Class template reverse_­view [range.reverse.view]

namespace std::ranges {
  template<View V>
    requires BidirectionalRange<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<ViewableRange R>
      requires BidirectionalRange<R> && Constructible<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 CommonRange<V>;
    constexpr reverse_iterator<iterator_t<const V>> begin() const
      requires CommonRange<const V>;

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

    constexpr auto size() requires SizedRange<V> {
      return ranges::size(base_);
    }
    constexpr auto size() const requires SizedRange<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<ViewableRange R> requires BidirectionalRange<R> && Constructible<V, all_view<R>> constexpr explicit reverse_view(R&& r);
Effects: Initializes base_­ with view::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 CommonRange<V>; constexpr reverse_iterator<iterator_t<const V>> begin() const requires CommonRange<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 CommonRange<const V>;
Effects: Equivalent to: return make_­reverse_­iterator(ranges::begin(base_­));

24.7.11.3 view​::​reverse [range.reverse.adaptor]

The name view::reverse denotes a range adaptor object.
For some subexpression E, the expression view::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}.