24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

24.7.1 General [range.adaptors.general]

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

24.7.2 Range adaptor objects [range.adaptor.object]

A range adaptor closure object is a unary function object that accepts a viewable_­range argument and returns a view.
For a range adaptor closure object C and an expression R such that decltype((R)) models viewable_­range, the following expressions are equivalent and yield a view: C(R) R | C
Given an additional range adaptor closure object D, the expression C | D produces another range adaptor closure object E.
E is a perfect forwarding call wrapper ([func.require]) with the following properties:
  • Its target object is an object d of type decay_­t<decltype((D))> direct-non-list-initialized with D.
  • It has one bound argument entity, an object c of type decay_­t<decltype((C))> direct-non-list-initialized with C.
  • Its call pattern is d(c(arg)), where arg is the argument used in a function call expression of E.
The expression C | D is well-formed if and only if the initializations of the state entities of E are all well-formed.
A range adaptor object is a customization point object ([customization.point.object]) that accepts a viewable_­range as its first argument and returns a view.
If a range adaptor object accepts only one argument, then it is a range adaptor closure object.
If a range adaptor object adaptor accepts more than one argument, then let range be an expression such that decltype((range)) models viewable_­range, let args... be arguments such that adaptor(range, args...) is a well-formed expression as specified in the rest of subclause [range.adaptors], and let BoundArgs be a pack that denotes decay_­t<decltype((args))>....
The expression adaptor(args...) produces a range adaptor closure object f that is a perfect forwarding call wrapper with the following properties:
  • Its target object is a copy of adaptor.
  • Its bound argument entities bound_­args consist of objects of types BoundArgs... direct-non-list-initialized with std​::​forward<decltype((args))>(args)..., respectively.
  • Its call pattern is adaptor(r, bound_­args...), where r is the argument used in a function call expression of f.
The expression adaptor(args...) is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.

24.7.3 Copyable wrapper [range.copy.wrap]

Many types in this subclause are specified in terms of an exposition-only class template copyable-box.
copyable-box<T> behaves exactly like optional<T> with the following differences:
  • copyable-box<T> constrains its type parameter T with copy_­constructible<T> && is_­object_­v<T>.
  • The default constructor of copyable-box<T> is equivalent to: constexpr copyable-box() noexcept(is_nothrow_default_constructible_v<T>) requires default_­initializable<T> : copyable-box{in_place} { }
  • If copyable<T> is not modeled, the copy assignment operator is equivalent to: copyable-box& operator=(const copyable-box& that) noexcept(is_nothrow_copy_constructible_v<T>) { if (this != addressof(that)) { if (that) emplace(*that); else reset(); } return *this; }
  • If movable<T> is not modeled, the move assignment operator is equivalent to: copyable-box& operator=(copyable-box&& that) noexcept(is_nothrow_move_constructible_v<T>) { if (this != addressof(that)) { if (that) emplace(std::move(*that)); else reset(); } return *this; }
Recommended practice: copyable-box<T> should store only a T if either T models copyable or is_­nothrow_­move_­constructible_­v<T> && is_­nothrow_­copy_­constructible_­v<T> is true.

24.7.4 Non-propagating cache [range.nonprop.cache]

Some types in subclause [range.adaptors] are specified in terms of an exposition-only class template non-propagating-​cache.
non-propagating-cache<T> behaves exactly like optional<T> with the following differences:
  • non-propagating-cache<T> constrains its type parameter T with is_­object_­v<T>.
  • The copy constructor is equivalent to: constexpr non-propagating-cache(const non-propagating-cache&) noexcept { }
  • The move constructor is equivalent to: constexpr non-propagating-cache(non-propagating-cache&& other) noexcept { other.reset(); }
  • The copy assignment operator is equivalent to: constexpr non-propagating-cache& operator=(const non-propagating-cache& other) noexcept { if (addressof(other) != this) reset(); return *this; }
  • The move assignment operator is equivalent to: constexpr non-propagating-cache& operator=(non-propagating-cache&& other) noexcept { reset(); other.reset(); return *this; }
  • non-propagating-cache<T> has an additional member function template specified as follows:
    template<class I> constexpr T& emplace-deref(const I& i); // exposition only
    Mandates: The declaration T t(*i); is well-formed for some invented variable t.
    [Note 1:
    If *i is a prvalue of type cv T, there is no requirement that it is movable ([dcl.init.general]).
    — end note]
    Effects: Calls reset().
    Then initializes the contained value as if direct-non-list-initializing an object of type T with the argument *i.
    Postconditions: *this contains a value.
    Returns: A reference to the new contained value.
    Throws: Any exception thrown by the initialization of the contained value.
    Remarks: If an exception is thrown during the initialization of T, *this does not contain a value, and the previous value (if any) has been destroyed.
[Note 2:
non-propagating-cache enables an input view to temporarily cache values as it is iterated over.
— end note]

24.7.5 All view [range.all]

24.7.5.1 General [range.all.general]

views​::​all returns a view that includes all elements of its range argument.
The name views​::​all denotes a range adaptor object ([range.adaptor.object]).
Given a subexpression E, the expression views​::​all(E) is expression-equivalent to:
  • decay-copy(E) if the decayed type of E models view.
  • Otherwise, ref_­view{E} if that expression is well-formed.
  • Otherwise, subrange{E}.

24.7.5.2 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_; // exposition only public: template<different-from<ref_view> T> requires see below constexpr ref_view(T&& t); constexpr R& base() const { return *r_; } constexpr iterator_t<R> begin() const { return ranges::begin(*r_); } constexpr sentinel_t<R> end() const { return ranges::end(*r_); } constexpr bool empty() const requires requires { ranges::empty(*r_); } { return ranges::empty(*r_); } constexpr auto size() const requires sized_­range<R> { return ranges::size(*r_); } constexpr auto data() const requires contiguous_­range<R> { return ranges::data(*r_); } }; template<class R> ref_view(R&) -> ref_view<R>; }
template<different-from<ref_view> T> requires see below constexpr ref_view(T&& t);
Effects: Initializes r_­ with addressof(static_­cast<R&>(std​::​forward<T>(t))).
Remarks: Let FUN denote the exposition-only functions void FUN(R&); void FUN(R&&) = delete;
The expression in the requires-clause is equivalent to: convertible_­to<T, R&> && requires { FUN(declval<T>()); }

24.7.6 Filter view [range.filter]

24.7.6.1 Overview [range.filter.overview]

filter_­view presents a view of the elements of an underlying sequence that satisfy a predicate.
The name views​::​filter denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and P, the expression views​::​filter(E, P) is expression-equivalent to filter_­view(E, P).
[Example 1: vector<int> is{ 0, 1, 2, 3, 4, 5, 6 }; auto evens = views::filter(is, [](int i) { return 0 == i % 2; }); for (int i : evens) cout << i << ' '; // prints: 0 2 4 6 — end example]

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

namespace std::ranges { template<input_­range V, indirect_­unary_­predicate<iterator_t<V>> Pred> requires view<V> && is_object_v<Pred> class filter_view : public view_interface<filter_view<V, Pred>> { private: V base_ = V(); // exposition only copyable-box<Pred> pred_; // exposition only // [range.filter.iterator], class filter_­view​::​iterator class iterator; // exposition only // [range.filter.sentinel], class filter_­view​::​sentinel class sentinel; // exposition only public: filter_view() requires default_­initializable<V> && default_­initializable<Pred> = default; constexpr filter_view(V base, Pred pred); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr const Pred& pred() const; constexpr iterator begin(); constexpr auto end() { if constexpr (common_­range<V>) return iterator{*this, ranges::end(base_)}; else return sentinel{*this}; } }; template<class R, class Pred> filter_view(R&&, Pred) -> filter_view<views::all_t<R>, Pred>; }
constexpr filter_view(V base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and initializes pred_­ with std​::​move(pred).
constexpr const Pred& pred() const;
Effects: Equivalent to: return *pred_­;
constexpr iterator begin();
Preconditions: pred_­.has_­value() is true.
Returns: {*this, ranges​::​find_­if(base_­, ref(*pred_­))}.
Remarks: In order to provide the amortized constant time complexity required by the range concept when filter_­view models forward_­range, this function caches the result within the filter_­view for use on subsequent calls.

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

namespace std::ranges { template<input_­range V, indirect_­unary_­predicate<iterator_t<V>> Pred> requires view<V> && is_object_v<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; // not always present using value_type = range_value_t<V>; using difference_type = range_difference_t<V>; iterator() requires default_­initializable<iterator_t<V>> = default; constexpr iterator(filter_view& parent, iterator_t<V> current); constexpr const iterator_t<V>& base() const &; constexpr iterator_t<V> base() &&; constexpr range_reference_t<V> operator*() const; constexpr iterator_t<V> operator->() const requires has-arrow<iterator_t<V>> && copyable<iterator_t<V>>; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires forward_­range<V>; constexpr iterator& operator--() requires bidirectional_­range<V>; constexpr iterator operator--(int) requires bidirectional_­range<V>; friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<V>>; friend constexpr range_rvalue_reference_t<V> iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(i.current_))); friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.current_, y.current_))) requires indirectly_­swappable<iterator_t<V>>; }; }
Modification of the element a filter_­view​::​iterator denotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.
iterator​::​iterator_­concept is defined as follows:
  • If V models bidirectional_­range, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if V models forward_­range, then iterator_­concept denotes forward_­iterator_­tag.
  • Otherwise, iterator_­concept denotes input_­iterator_­tag.
The member typedef-name iterator_­category is defined if and only if V models forward_­range.
In that case, iterator​::​iterator_­category is defined as follows:
  • Let C denote the type iterator_­traits<iterator_­t<V>>​::​iterator_­category.
  • If C models derived_­from<bidirectional_­iterator_­tag>, then iterator_­category denotes bidirectional_­iterator_­tag.
  • Otherwise, if C models derived_­from<forward_­iterator_­tag>, then iterator_­category denotes forward_­iterator_­tag.
  • Otherwise, iterator_­category denotes C.
constexpr iterator(filter_view& parent, iterator_t<V> current);
Effects: Initializes current_­ with std​::​move(current) and parent_­ with addressof(parent).
constexpr const iterator_t<V>& base() const &;
Effects: Equivalent to: return current_­;
constexpr iterator_t<V> base() &&;
Effects: Equivalent to: return std​::​move(current_­);
constexpr range_reference_t<V> operator*() const;
Effects: Equivalent to: return *current_­;
constexpr iterator_t<V> operator->() const requires has-arrow<iterator_t<V>> && copyable<iterator_t<V>>;
Effects: Equivalent to: return current_­;
constexpr iterator& operator++();
Effects: Equivalent to: current_ = ranges::find_if(std::move(++current_), ranges::end(parent_->base_), ref(*parent_->pred_)); return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
constexpr iterator operator++(int) requires forward_­range<V>;
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires bidirectional_­range<V>;
Effects: Equivalent to: do --current_; while (!invoke(*parent_->pred_, *current_)); return *this;
constexpr iterator operator--(int) requires bidirectional_­range<V>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<V>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr range_rvalue_reference_t<V> iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(i.current_­)));
Effects: Equivalent to: return ranges​::​iter_­move(i.current_­);
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.current_­, y.current_­))) requires indirectly_­swappable<iterator_t<V>>;
Effects: Equivalent to ranges​::​iter_­swap(x.current_­, y.current_­).

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

namespace std::ranges { template<input_­range V, indirect_­unary_­predicate<iterator_t<V>> Pred> requires view<V> && is_object_v<Pred> class filter_view<V, Pred>::sentinel { private: sentinel_t<V> end_ = sentinel_t<V>(); // exposition only public: sentinel() = default; constexpr explicit sentinel(filter_view& parent); constexpr sentinel_t<V> base() const; friend constexpr bool operator==(const iterator& x, const sentinel& y); }; }
constexpr explicit sentinel(filter_view& parent);
Effects: Initializes end_­ with ranges​::​end(parent.base_­).
constexpr sentinel_t<V> base() const;
Effects: Equivalent to: return end_­;
friend constexpr bool operator==(const iterator& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ == y.end_­;

24.7.7 Transform view [range.transform]

24.7.7.1 Overview [range.transform.overview]

transform_­view presents a view of an underlying sequence after applying a transformation function to each element.
The name views​::​transform denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​transform(E, F) is expression-equivalent to transform_­view(E, F).
[Example 1: vector<int> is{ 0, 1, 2, 3, 4 }; auto squares = views::transform(is, [](int i) { return i * i; }); for (int i : squares) cout << i << ' '; // prints: 0 1 4 9 16 — end example]

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

namespace std::ranges { template<input_­range V, copy_­constructible F> requires view<V> && is_object_v<F> && regular_­invocable<F&, range_reference_t<V>> && can-reference<invoke_result_t<F&, range_reference_t<V>>> class transform_view : public view_interface<transform_view<V, F>> { private: // [range.transform.iterator], class template transform_­view​::​iterator template<bool> struct iterator; // exposition only // [range.transform.sentinel], class template transform_­view​::​sentinel template<bool> struct sentinel; // exposition only V base_ = V(); // exposition only copyable-box<F> fun_; // exposition only public: transform_view() requires default_­initializable<V> && default_­initializable<F> = default; constexpr transform_view(V base, F fun); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr iterator<false> begin(); constexpr iterator<true> begin() const requires range<const V> && regular_­invocable<const F&, range_reference_t<const V>>; constexpr sentinel<false> end(); constexpr iterator<false> end() requires common_­range<V>; constexpr sentinel<true> end() const requires range<const V> && regular_­invocable<const F&, range_reference_t<const V>>; constexpr iterator<true> end() const requires common_­range<const V> && regular_­invocable<const F&, range_reference_t<const V>>; constexpr auto size() requires sized_­range<V> { return ranges::size(base_); } constexpr auto size() const requires sized_­range<const V> { return ranges::size(base_); } }; template<class R, class F> transform_view(R&&, F) -> transform_view<views::all_t<R>, F>; }
constexpr transform_view(V base, F fun);
Effects: Initializes base_­ with std​::​move(base) and fun_­ with std​::​move(fun).
constexpr iterator<false> begin();
Effects: Equivalent to: return iterator<false>{*this, ranges::begin(base_)};
constexpr iterator<true> begin() const requires range<const V> && regular_­invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to: return iterator<true>{*this, ranges::begin(base_)};
constexpr sentinel<false> end();
Effects: Equivalent to: return sentinel<false>{ranges::end(base_)};
constexpr iterator<false> end() requires common_­range<V>;
Effects: Equivalent to: return iterator<false>{*this, ranges::end(base_)};
constexpr sentinel<true> end() const requires range<const V> && regular_­invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to: return sentinel<true>{ranges::end(base_)};
constexpr iterator<true> end() const requires common_­range<const V> && regular_­invocable<const F&, range_reference_t<const V>>;
Effects: Equivalent to: return iterator<true>{*this, ranges::end(base_)};

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

namespace std::ranges { template<input_­range V, copy_­constructible F> requires view<V> && is_object_v<F> && regular_­invocable<F&, range_reference_t<V>> && can-reference<invoke_result_t<F&, range_reference_t<V>>> template<bool Const> class transform_view<V, F>::iterator { private: using Parent = maybe-const<Const, transform_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only Parent* parent_ = nullptr; // exposition only public: using iterator_concept = see below; using iterator_category = see below; // not always present using value_type = remove_cvref_t<invoke_result_t<F&, range_reference_t<Base>>>; using difference_type = range_difference_t<Base>; iterator() requires default_­initializable<iterator_t<Base>> = default; constexpr iterator(Parent& parent, iterator_t<Base> current); constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>; constexpr const iterator_t<Base>& base() const &; constexpr iterator_t<Base> base() &&; constexpr decltype(auto) operator*() const { return invoke(*parent_->fun_, *current_); } constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires forward_­range<Base>; constexpr iterator& operator--() requires bidirectional_­range<Base>; constexpr iterator operator--(int) requires bidirectional_­range<Base>; constexpr iterator& operator+=(difference_type n) requires random_­access_­range<Base>; constexpr iterator& operator-=(difference_type n) requires random_­access_­range<Base>; constexpr decltype(auto) operator[](difference_type n) const requires random_­access_­range<Base> { return invoke(*parent_->fun_, current_[n]); } friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<Base>>; friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires random_­access_­range<Base> && three_­way_­comparable<iterator_t<Base>>; friend constexpr iterator operator+(iterator i, difference_type n) requires random_­access_­range<Base>; friend constexpr iterator operator+(difference_type n, iterator i) requires random_­access_­range<Base>; friend constexpr iterator operator-(iterator i, difference_type n) requires random_­access_­range<Base>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires sized_­sentinel_­for<iterator_t<Base>, iterator_t<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; } }; }
iterator​::​iterator_­concept is defined as follows:
The member typedef-name iterator_­category is defined if and only if Base models forward_­range.
In that case, iterator​::​iterator_­category is defined as follows: Let C denote the type iterator_­traits<iterator_­t<Base>>​::​iterator_­category.
  • If is_­lvalue_­reference_­v<invoke_­result_­t<F&, range_­reference_­t<Base>>> is true, then
    • if C models derived_­from<contiguous_­iterator_­tag>, iterator_­category denotes random_­access_­iterator_­tag;
    • otherwise, iterator_­category denotes C.
  • Otherwise, iterator_­category denotes input_­iterator_­tag.
constexpr iterator(Parent& parent, iterator_t<Base> current);
Effects: Initializes current_­ with std​::​move(current) and parent_­ with addressof(parent).
constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>;
Effects: Initializes current_­ with std​::​move(i.current_­) and parent_­ with i.parent_­.
constexpr const iterator_t<Base>& base() const &;
Effects: Equivalent to: return current_­;
constexpr iterator_t<Base> base() &&;
Effects: Equivalent to: return std​::​move(current_­);
constexpr iterator& operator++();
Effects: Equivalent to: ++current_; return *this;
constexpr void operator++(int);
Effects: Equivalent to ++current_­.
constexpr iterator operator++(int) requires forward_­range<Base>;
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires bidirectional_­range<Base>;
Effects: Equivalent to: --current_; return *this;
constexpr iterator operator--(int) requires bidirectional_­range<Base>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
constexpr iterator& operator+=(difference_type n) requires random_­access_­range<Base>;
Effects: Equivalent to: current_ += n; return *this;
constexpr iterator& operator-=(difference_type n) requires random_­access_­range<Base>;
Effects: Equivalent to: current_ -= n; return *this;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return x.current_­ < y.current_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return !(x < y);
friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires random_­access_­range<Base> && three_­way_­comparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ <=> y.current_­;
friend constexpr iterator operator+(iterator i, difference_type n) requires random_­access_­range<Base>; friend constexpr iterator operator+(difference_type n, iterator i) requires random_­access_­range<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ + n};
friend constexpr iterator operator-(iterator i, difference_type n) requires random_­access_­range<Base>;
Effects: Equivalent to: return iterator{*i.parent_­, i.current_­ - n};
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires sized_­sentinel_­for<iterator_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ - y.current_­;

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

namespace std::ranges { template<input_­range V, copy_­constructible F> requires view<V> && is_object_v<F> && regular_­invocable<F&, range_reference_t<V>> && can-reference<invoke_result_t<F&, range_reference_t<V>>> template<bool Const> class transform_view<V, F>::sentinel { private: using Parent = maybe-const<Const, transform_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only public: sentinel() = default; constexpr explicit sentinel(sentinel_t<Base> end); constexpr sentinel(sentinel<!Const> i) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& y, const iterator<OtherConst>& x); }; }
constexpr explicit sentinel(sentinel_t<Base> end);
Effects: Initializes end_­ with end.
constexpr sentinel(sentinel<!Const> i) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(i.end_­).
constexpr sentinel_t<Base> base() const;
Effects: Equivalent to: return end_­;
template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ == y.end_­;
template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ - y.end_­;
template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& y, const iterator<OtherConst>& x);
Effects: Equivalent to: return y.end_­ - x.current_­;

24.7.8 Take view [range.take]

24.7.8.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.
The name views​::​take denotes a range adaptor object ([range.adaptor.object]).
Let E and F be expressions, let T be remove_­cvref_­t<decltype((E))>, and let D be range_­difference_­t<decltype((E))>.
If decltype((F)) does not model convertible_­to<D>, views​::​take(E, F) is ill-formed.
Otherwise, the expression views​::​take(E, F) is expression-equivalent to:
[Example 1: vector<int> is{0,1,2,3,4,5,6,7,8,9}; for (int i : is | views::take(5)) cout << i << ' '; // prints: 0 1 2 3 4 — end example]

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

namespace std::ranges { template<view V> class take_view : public view_interface<take_view<V>> { private: V base_ = V(); // exposition only range_difference_t<V> count_ = 0; // exposition only // [range.take.sentinel], class template take_­view​::​sentinel template<bool> struct sentinel; // exposition only public: take_view() requires default_­initializable<V> = default; constexpr take_view(V base, range_difference_t<V> count); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() requires (!simple-view<V>) { if constexpr (sized_­range<V>) { if constexpr (random_­access_­range<V>) return ranges::begin(base_); else { auto sz = size(); return counted_iterator(ranges::begin(base_), sz); } } else return counted_iterator(ranges::begin(base_), count_); } constexpr auto begin() const requires range<const V> { if constexpr (sized_­range<const V>) { if constexpr (random_­access_­range<const V>) return ranges::begin(base_); else { auto sz = size(); return counted_iterator(ranges::begin(base_), sz); } } else return counted_iterator(ranges::begin(base_), count_); } constexpr auto end() requires (!simple-view<V>) { if constexpr (sized_­range<V>) { if constexpr (random_­access_­range<V>) return ranges::begin(base_) + size(); else return default_sentinel; } else return sentinel<false>{ranges::end(base_)}; } constexpr auto end() const requires range<const V> { if constexpr (sized_­range<const V>) { if constexpr (random_­access_­range<const V>) return ranges::begin(base_) + size(); else return default_sentinel; } else return sentinel<true>{ranges::end(base_)}; } constexpr auto size() requires sized_­range<V> { auto n = ranges::size(base_); return ranges::min(n, static_cast<decltype(n)>(count_)); } constexpr auto size() const requires sized_­range<const V> { auto n = ranges::size(base_); return ranges::min(n, static_cast<decltype(n)>(count_)); } }; template<class R> take_view(R&&, range_difference_t<R>) -> take_view<views::all_t<R>>; }
constexpr take_view(V base, range_difference_t<V> count);
Effects: Initializes base_­ with std​::​move(base) and count_­ with count.

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

namespace std::ranges { template<view V> template<bool Const> class take_view<V>::sentinel { private: using Base = maybe-const<Const, V>; // exposition only template<bool OtherConst> using CI = counted_iterator<iterator_t<maybe-const<OtherConst, 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> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const; friend constexpr bool operator==(const CI<Const>& y, const sentinel& x); template<bool OtherConst = !Const> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const CI<OtherConst>& y, const sentinel& x); }; }
constexpr explicit sentinel(sentinel_t<Base> end);
Effects: Initializes end_­ with end.
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(s.end_­).
constexpr sentinel_t<Base> base() const;
Effects: Equivalent to: return end_­;
friend constexpr bool operator==(const CI<Const>& y, const sentinel& x); template<bool OtherConst = !Const> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const CI<OtherConst>& y, const sentinel& x);
Effects: Equivalent to: return y.count() == 0 || y.base() == x.end_­;

24.7.9 Take while view [range.take.while]

24.7.9.1 Overview [range.take.while.overview]

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

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

namespace std::ranges { template<view V, class Pred> requires input_­range<V> && is_object_v<Pred> && indirect_­unary_­predicate<const Pred, iterator_t<V>> class take_while_view : public view_interface<take_while_view<V, Pred>> { // [range.take.while.sentinel], class template take_­while_­view​::​sentinel template<bool> class sentinel; // exposition only V base_ = V(); // exposition only copyable-box<Pred> pred_; // exposition only public: take_while_view() requires default_­initializable<V> && default_­initializable<Pred> = default; constexpr take_while_view(V base, Pred pred); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr const Pred& pred() const; constexpr auto begin() requires (!simple-view<V>) { return ranges::begin(base_); } constexpr auto begin() const requires range<const V> && indirect_­unary_­predicate<const Pred, iterator_t<const V>> { return ranges::begin(base_); } constexpr auto end() requires (!simple-view<V>) { return sentinel<false>(ranges::end(base_), addressof(*pred_)); } constexpr auto end() const requires range<const V> && indirect_­unary_­predicate<const Pred, iterator_t<const V>> { return sentinel<true>(ranges::end(base_), addressof(*pred_)); } }; template<class R, class Pred> take_while_view(R&&, Pred) -> take_while_view<views::all_t<R>, Pred>; }
constexpr take_while_view(V base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and pred_­ with std​::​move(pred).
constexpr const Pred& pred() const;
Effects: Equivalent to: return *pred_­;

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

namespace std::ranges { template<view V, class Pred> requires input_­range<V> && is_object_v<Pred> && indirect_­unary_­predicate<const Pred, iterator_t<V>> template<bool Const> class take_while_view<V, Pred>::sentinel { // exposition only using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only const Pred* pred_ = nullptr; // exposition only public: sentinel() = default; constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred); constexpr sentinel(sentinel<!Const> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const { return end_; } friend constexpr bool operator==(const iterator_t<Base>& x, const sentinel& y); template<bool OtherConst = !Const> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator_t<maybe-const<OtherConst, V>>& x, const sentinel& y); }; }
constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
Effects: Initializes end_­ with end and pred_­ with pred.
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with s.end_­ and pred_­ with s.pred_­.
friend constexpr bool operator==(const iterator_t<Base>& x, const sentinel& y); template<bool OtherConst = !Const> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator_t<maybe-const<OtherConst, V>>& x, const sentinel& y);
Effects: Equivalent to: return y.end_­ == x || !invoke(*y.pred_­, *x);

24.7.10 Drop view [range.drop]

24.7.10.1 Overview [range.drop.overview]

drop_­view produces a view excluding the first N elements from another view, or an empty range if the adapted view contains fewer than N elements.
The name views​::​drop denotes a range adaptor object ([range.adaptor.object]).
Let E and F be expressions, let T be remove_­cvref_­t<decltype((E))>, and let D be range_­difference_­t<decltype((E))>.
If decltype((F)) does not model convertible_­to<D>, views​::​drop(E, F) is ill-formed.
Otherwise, the expression views​::​drop(E, F) is expression-equivalent to:
[Example 1: auto ints = views::iota(0) | views::take(10); for (auto i : ints | views::drop(5)) { cout << i << ' '; // prints 5 6 7 8 9 } — end example]

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

namespace std::ranges { template<view V> class drop_view : public view_interface<drop_view<V>> { public: drop_view() requires default_­initializable<V> = default; constexpr drop_view(V base, range_difference_t<V> count); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() requires (!(simple-view<V> && random_­access_­range<const V> && sized_­range<const V>)); constexpr auto begin() const requires random_­access_­range<const V> && sized_­range<const V>; constexpr auto end() requires (!simple-view<V>) { return ranges::end(base_); } constexpr auto end() const requires range<const V> { return ranges::end(base_); } constexpr auto size() requires sized_­range<V> { const auto s = ranges::size(base_); const auto c = static_cast<decltype(s)>(count_); return s < c ? 0 : s - c; } constexpr auto size() const requires sized_­range<const V> { const auto s = ranges::size(base_); const auto c = static_cast<decltype(s)>(count_); return s < c ? 0 : s - c; } private: V base_ = V(); // exposition only range_difference_t<V> count_ = 0; // exposition only }; template<class R> drop_view(R&&, range_difference_t<R>) -> drop_view<views::all_t<R>>; }
constexpr drop_view(V base, range_difference_t<V> count);
Preconditions: count >= 0 is true.
Effects: Initializes base_­ with std​::​move(base) and count_­ with count.
constexpr auto begin() requires (!(simple-view<V> && random_­access_­range<const V> && sized_­range<const V>)); constexpr auto begin() const requires random_­access_­range<const V> && sized_­range<const V>;
Returns: ranges​::​next(ranges​::​begin(base_­), count_­, ranges​::​end(base_­)).
Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_­view models forward_­range, the first overload caches the result within the drop_­view for use on subsequent calls.
[Note 1:
Without this, applying a reverse_­view over a drop_­view would have quadratic iteration complexity.
— end note]

24.7.11 Drop while view [range.drop.while]

24.7.11.1 Overview [range.drop.while.overview]

Given a unary predicate pred and a view r, drop_­while_­view produces a view of the range [ranges​::​find_­if_­not(r, pred), ranges​::​end(r)).
The name views​::​drop_­while denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​drop_­while(E, F) is expression-equivalent to drop_­while_­view(E, F).
[Example 1: constexpr auto source = " \t \t \t hello there"; auto is_invisible = [](const auto x) { return x == ' ' || x == '\t'; }; auto skip_ws = views::drop_while(source, is_invisible); for (auto c : skip_ws) { cout << c; // prints hello there with no leading space } — end example]

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

namespace std::ranges { template<view V, class Pred> requires input_­range<V> && is_object_v<Pred> && indirect_­unary_­predicate<const Pred, iterator_t<V>> class drop_while_view : public view_interface<drop_while_view<V, Pred>> { public: drop_while_view() requires default_­initializable<V> && default_­initializable<Pred> = default; constexpr drop_while_view(V base, Pred pred); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr const Pred& pred() const; constexpr auto begin(); constexpr auto end() { return ranges::end(base_); } private: V base_ = V(); // exposition only copyable-box<Pred> pred_; // exposition only }; template<class R, class Pred> drop_while_view(R&&, Pred) -> drop_while_view<views::all_t<R>, Pred>; }
constexpr drop_while_view(V base, Pred pred);
Effects: Initializes base_­ with std​::​move(base) and pred_­ with std​::​move(pred).
constexpr const Pred& pred() const;
Effects: Equivalent to: return *pred_­;
constexpr auto begin();
Preconditions: pred_­.has_­value() is true.
Returns: ranges​::​find_­if_­not(base_­, cref(*pred_­)).
Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_­while_­view models forward_­range, the first call caches the result within the drop_­while_­view for use on subsequent calls.
[Note 1:
Without this, applying a reverse_­view over a drop_­while_­view would have quadratic iteration complexity.
— end note]

24.7.12 Join view [range.join]

24.7.12.1 Overview [range.join.overview]

join_­view flattens a view of ranges into a view.
The name views​::​join denotes a range adaptor object ([range.adaptor.object]).
Given a subexpression E, the expression views​::​join(E) is expression-equivalent to join_­view<views​::​all_­t<decltype((E))>>{E}.
[Example 1: vector<string> ss{"hello", " ", "world", "!"}; for (char ch : ss | views::join) cout << ch; // prints: hello world! — end example]

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

namespace std::ranges { template<input_­range V> requires view<V> && input_­range<range_reference_t<V>> class join_view : public view_interface<join_view<V>> { private: using InnerRng = range_reference_t<V>; // exposition only // [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 non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only // when !is_­reference_­v<InnerRng> public: join_view() requires default_­initializable<V> = default; constexpr explicit join_view(V base); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() { constexpr bool use_const = simple-view<V> && is_reference_v<range_reference_t<V>>; return iterator<use_const>{*this, ranges::begin(base_)}; } constexpr auto begin() const requires input_­range<const V> && is_reference_v<range_reference_t<const V>> { return iterator<true>{*this, ranges::begin(base_)}; } constexpr auto end() { if constexpr (forward_­range<V> && is_reference_v<InnerRng> && forward_­range<InnerRng> && common_­range<V> && common_­range<InnerRng>) return iterator<simple-view<V>>{*this, ranges::end(base_)}; else return sentinel<simple-view<V>>{*this}; } constexpr auto end() const requires input_­range<const V> && is_reference_v<range_reference_t<const V>> { if constexpr (forward_­range<const V> && is_reference_v<range_reference_t<const V>> && forward_­range<range_reference_t<const V>> && common_­range<const V> && common_­range<range_reference_t<const V>>) return iterator<true>{*this, ranges::end(base_)}; else return sentinel<true>{*this}; } }; template<class R> explicit join_view(R&&) -> join_view<views::all_t<R>>; }
constexpr explicit join_view(V base);
Effects: Initializes base_­ with std​::​move(base).

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

namespace std::ranges { template<input_­range V> requires view<V> && input_­range<range_reference_t<V>> template<bool Const> struct join_view<V>::iterator { private: using Parent = maybe-const<Const, join_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only using OuterIter = iterator_t<Base>; // exposition only using InnerIter = iterator_t<range_reference_t<Base>>; // exposition only static constexpr bool ref-is-glvalue = // exposition only is_reference_v<range_reference_t<Base>>; OuterIter outer_ = OuterIter(); // exposition only InnerIter inner_ = InnerIter(); // exposition only Parent* parent_ = nullptr; // exposition only constexpr void satisfy(); // exposition only public: using iterator_concept = see below; using iterator_category = see below; // not always present using value_type = range_value_t<range_reference_t<Base>>; using difference_type = see below; iterator() requires default_­initializable<OuterIter> && default_­initializable<InnerIter> = default; constexpr iterator(Parent& parent, OuterIter outer); constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, OuterIter> && convertible_­to<iterator_t<InnerRng>, InnerIter>; constexpr decltype(auto) operator*() const { return *inner_; } constexpr InnerIter operator->() const requires has-arrow<InnerIter> && copyable<InnerIter>; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires ref-is-glvalue && forward_­range<Base> && forward_­range<range_reference_t<Base>>; constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_­range<Base> && bidirectional_­range<range_reference_t<Base>> && common_­range<range_reference_t<Base>>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_­range<Base> && bidirectional_­range<range_reference_t<Base>> && common_­range<range_reference_t<Base>>; friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && equality_­comparable<iterator_t<Base>> && equality_­comparable<iterator_t<range_reference_t<Base>>>; friend constexpr decltype(auto) iter_move(const iterator& i) noexcept(noexcept(ranges::iter_move(i.inner_))) { return ranges::iter_move(i.inner_); } friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_))) requires indirectly_­swappable<InnerIter>; }; }
iterator​::​iterator_­concept is defined as follows:
  • If ref-is-glvalue is true and Base and range_­reference_­t<Base> each model bidirectional_­range, then iterator_­concept denotes bidirectional_­iterator_­tag.
  • Otherwise, if ref-is-glvalue is true and Base and range_­reference_­t<Base> each model forward_­range, then iterator_­concept denotes forward_­iterator_­tag.
  • Otherwise, iterator_­concept denotes input_­iterator_­tag.
The member typedef-name iterator_­category is defined if and only if ref-is-glvalue is true, Base models forward_­range, and range_­reference_­t<Base> models forward_­range.
In that case, iterator​::​iterator_­category is defined as follows:
  • Let OUTERC denote iterator_­traits<iterator_­t<Base>>​::​iterator_­category, and let INNERC denote iterator_­traits<iterator_­t<range_­reference_­t<Base>>>​::​iterator_­category.
  • If OUTERC and INNERC each model derived_­from<bidirectional_­iterator_­tag>, iterator_­category denotes bidirectional_­iterator_­tag.
  • Otherwise, if OUTERC and INNERC each model derived_­from<forward_­iterator_­tag>, iterator_­category denotes forward_­iterator_­tag.
  • Otherwise, iterator_­category denotes input_­iterator_­tag.
iterator​::​difference_­type denotes the type: common_type_t< range_difference_t<Base>, range_difference_t<range_reference_t<Base>>>
join_­view iterators use the satisfy function to skip over empty inner ranges.
constexpr void satisfy(); // exposition only
Effects: Equivalent to: auto update_inner = [this](const iterator_t<Base>& x) -> auto&& { if constexpr (ref-is-glvalue) // *x is a reference return *x; else return parent_->inner_.emplace-deref(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_ = InnerIter();
constexpr iterator(Parent& parent, OuterIter outer);
Effects: Initializes outer_­ with std​::​move(outer) and parent_­ with addressof(parent); then calls satisfy().
constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, OuterIter> && convertible_­to<iterator_t<InnerRng>, InnerIter>;
Effects: Initializes outer_­ with std​::​move(i.outer_­), inner_­ with std​::​move(i.inner_­), and parent_­ with i.parent_­.
constexpr InnerIter operator->() const requires has-arrow<InnerIter> && copyable<InnerIter>;
Effects: Equivalent to return inner_­;
constexpr iterator& operator++();
Let inner-range be:
  • If ref-is-glvalue is true, *outer_­.
  • Otherwise, *parent_­->inner_­.
Effects: Equivalent to: auto&& inner_rng = inner-range; if (++inner_ == ranges::end(inner_rng)) { ++outer_; satisfy(); } return *this;
constexpr void operator++(int);
Effects: Equivalent to: ++*this.
constexpr iterator operator++(int) requires ref-is-glvalue && forward_­range<Base> && forward_­range<range_reference_t<Base>>;
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_­range<Base> && bidirectional_­range<range_reference_t<Base>> && common_­range<range_reference_t<Base>>;
Effects: Equivalent to: if (outer_ == ranges::end(parent_->base_)) inner_ = ranges::end(*--outer_); while (inner_ == ranges::begin(*outer_)) inner_ = ranges::end(*--outer_); --inner_; return *this;
constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_­range<Base> && bidirectional_­range<range_reference_t<Base>> && common_­range<range_reference_t<Base>>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && equality_­comparable<iterator_t<Base>> && equality_­comparable<iterator_t<range_reference_t<Base>>>;
Effects: Equivalent to: return x.outer_­ == y.outer_­ && x.inner_­ == y.inner_­;
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(x.inner_­, y.inner_­))) requires indirectly_­swappable<InnerIter>;
Effects: Equivalent to: return ranges​::​iter_­swap(x.inner_­, y.inner_­);

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

namespace std::ranges { template<input_­range V> requires view<V> && input_­range<range_reference_t<V>> template<bool Const> struct join_view<V>::sentinel { private: using Parent = maybe-const<Const, join_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only public: sentinel() = default; constexpr explicit sentinel(Parent& parent); constexpr sentinel(sentinel<!Const> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>; template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); }; }
constexpr explicit sentinel(Parent& parent);
Effects: Initializes end_­ with ranges​::​end(parent.base_­).
constexpr sentinel(sentinel<!Const> s) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(s.end_­).
template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
Effects: Equivalent to: return x.outer_­ == y.end_­;

24.7.13 Lazy split view [range.lazy.split]

24.7.13.1 Overview [range.lazy.split.overview]

lazy_­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.
The name views​::​lazy_­split denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​lazy_­split(E, F) is expression-equivalent to lazy_­split_­view(E, F).
[Example 1: string str{"the quick brown fox"}; for (auto word : str | views::lazy_split(' ')) { for (char ch : word) cout << ch; cout << '*'; } // The above prints: the*quick*brown*fox* — end example]

24.7.13.2 Class template lazy_­split_­view [range.lazy.split.view]

namespace std::ranges { template<auto> struct require-constant; // exposition only template<class R> concept tiny-range = // exposition only sized_­range<R> && requires { typename require-constant<remove_reference_t<R>::size()>; } && (remove_reference_t<R>::size() <= 1); template<input_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_­range<V> || tiny-range<Pattern>) class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> { private: V base_ = V(); // exposition only Pattern pattern_ = Pattern(); // exposition only non-propagating-cache<iterator_t<V>> current_; // exposition only, present only // if !forward_­range<V> // [range.lazy.split.outer], class template lazy_­split_­view​::​outer-iterator template<bool> struct outer-iterator; // exposition only // [range.lazy.split.inner], class template lazy_­split_­view​::​inner-iterator template<bool> struct inner-iterator; // exposition only public: lazy_split_view() requires default_­initializable<V> && default_­initializable<Pattern> = default; constexpr lazy_split_view(V base, Pattern pattern); template<input_­range R> requires constructible_­from<V, views::all_t<R>> && constructible_­from<Pattern, single_view<range_value_t<R>>> constexpr lazy_split_view(R&& r, range_value_t<R> e); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() { if constexpr (forward_­range<V>) return outer-iterator<simple-view<V>>{*this, ranges::begin(base_)}; else { current_ = ranges::begin(base_); return outer-iterator<false>{*this}; } } constexpr auto begin() const requires forward_­range<V> && forward_­range<const V> { return outer-iterator<true>{*this, ranges::begin(base_)}; } constexpr auto end() requires forward_­range<V> && common_­range<V> { return outer-iterator<simple-view<V>>{*this, ranges::end(base_)}; } constexpr auto end() const { if constexpr (forward_­range<V> && forward_­range<const V> && common_­range<const V>) return outer-iterator<true>{*this, ranges::end(base_)}; else return default_sentinel; } }; template<class R, class P> lazy_split_view(R&&, P&&) -> lazy_split_view<views::all_t<R>, views::all_t<P>>; template<input_­range R> lazy_split_view(R&&, range_value_t<R>) -> lazy_split_view<views::all_t<R>, single_view<range_value_t<R>>>; }
constexpr lazy_split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std​::​move(base), and pattern_­ with std​::​move(pattern).
template<input_­range R> requires constructible_­from<V, views::all_t<R>> && constructible_­from<Pattern, single_view<range_value_t<R>>> constexpr lazy_split_view(R&& r, range_value_t<R> e);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)), and pattern_­ with views​::​
single(std​::​move(e))
.

24.7.13.3 Class template lazy_­split_­view​::​outer-iterator [range.lazy.split.outer]

namespace std::ranges { template<input_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_­range<V> || tiny-range<Pattern>) template<bool Const> struct lazy_split_view<V, Pattern>::outer-iterator { private: using Parent = maybe-const<Const, lazy_split_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only Parent* parent_ = nullptr; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only, present only // if V models forward_­range bool trailing_empty_ = false; // exposition only public: using iterator_concept = conditional_t<forward_­range<Base>, forward_iterator_tag, input_iterator_tag>; using iterator_category = input_iterator_tag; // present only if Base // models forward_­range // [range.lazy.split.outer.value], class lazy_­split_­view​::​outer-iterator​::​value_­type struct value_type; using difference_type = range_difference_t<Base>; outer-iterator() = default; constexpr explicit outer-iterator(Parent& parent) requires (!forward_­range<Base>); constexpr outer-iterator(Parent& parent, iterator_t<Base> current) requires forward_­range<Base>; constexpr outer-iterator(outer-iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>; constexpr value_type operator*() const; constexpr outer-iterator& operator++(); constexpr decltype(auto) operator++(int) { if constexpr (forward_­range<Base>) { auto tmp = *this; ++*this; return tmp; } else ++*this; } friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y) requires forward_­range<Base>; friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t); }; }
Many of the specifications in [range.lazy.split] refer to the notional member current of outer-iterator.
current is equivalent to current_­ if V models forward_­range, and *parent_­->current_­ otherwise.
constexpr explicit outer-iterator(Parent& parent) requires (!forward_­range<Base>);
Effects: Initializes parent_­ with addressof(parent).
constexpr outer-iterator(Parent& parent, iterator_t<Base> current) requires forward_­range<Base>;
Effects: Initializes parent_­ with addressof(parent) and current_­ with std​::​move(current).
constexpr outer-iterator(outer-iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>;
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) { trailing_empty_ = false; return *this; } const auto [pbegin, pend] = subrange{parent_->pattern_}; if (pbegin == pend) ++current; else if constexpr (tiny-range<Pattern>) { current = ranges::find(std::move(current), end, *pbegin); if (current != end) { ++current; if (current == end) trailing_empty_ = true; } } else { do { auto [b, p] = ranges::mismatch(current, end, pbegin, pend); if (p == pend) { current = b; if (current == end) trailing_empty_ = true; break; // The pattern matched; skip it } } while (++current != end); } return *this;
friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y) requires forward_­range<Base>;
Effects: Equivalent to: return x.current_ == y.current_ && x.trailing_empty_ == y.trailing_empty_;
friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);
Effects: Equivalent to: return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;

24.7.13.4 Class lazy_­split_­view​::​outer-iterator​::​value_­type [range.lazy.split.outer.value]

namespace std::ranges { template<input_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_­range<V> || tiny-range<Pattern>) template<bool Const> struct lazy_split_view<V, Pattern>::outer-iterator<Const>::value_type : view_interface<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 std​::​move(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.13.5 Class template lazy_­split_­view​::​inner-iterator [range.lazy.split.inner]

namespace std::ranges { template<input_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_­range<V> || tiny-range<Pattern>) template<bool Const> struct lazy_split_view<V, Pattern>::inner-iterator { private: using Base = maybe-const<Const, 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; // present only if Base // models forward_­range using value_type = range_value_t<Base>; using difference_type = range_difference_t<Base>; inner-iterator() = default; constexpr explicit inner-iterator(outer-iterator<Const> i); constexpr const iterator_t<Base>& base() const &; constexpr iterator_t<Base> base() &&; constexpr decltype(auto) operator*() const { return *i_.current; } constexpr inner-iterator& operator++(); constexpr decltype(auto) operator++(int) { if constexpr (forward_­range<Base>) { auto tmp = *this; ++*this; return tmp; } else ++*this; } friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y) requires forward_­range<Base>; friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t); friend constexpr decltype(auto) iter_move(const inner-iterator& i) noexcept(noexcept(ranges::iter_move(i.i_.current))) { return ranges::iter_move(i.i_.current); } friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) noexcept(noexcept(ranges::iter_swap(x.i_.current, y.i_.current))) requires indirectly_­swappable<iterator_t<Base>>; }; }
If Base does not model forward_­range there is no member iterator_­category.
Otherwise, the typedef-name iterator_­category denotes:
  • forward_­iterator_­tag if iterator_­traits<iterator_­t<Base>>​::​iterator_­category models
    derived_­from<forward_­iterator_­tag>;
  • otherwise, iterator_­traits<iterator_­t<Base>>​::​iterator_­category.
constexpr explicit inner-iterator(outer-iterator<Const> i);
Effects: Initializes i_­ with std​::​move(i).
constexpr const iterator_t<Base>& base() const &;
Effects: Equivalent to: return i_­.current;
constexpr iterator_t<Base> base() &&;
Effects: Equivalent to: return std​::​move(i_­.current);
constexpr inner-iterator& operator++();
Effects: Equivalent to: incremented_ = true; if constexpr (!forward_­range<Base>) { if constexpr (Pattern::size() == 0) { return *this; } } ++i_.current; return *this;
friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y) requires forward_­range<Base>;
Effects: Equivalent to: return x.i_­.current == y.i_­.current;
friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);
Effects: Equivalent to: auto [pcur, pend] = subrange{x.i_.parent_->pattern_}; auto end = ranges::end(x.i_.parent_->base_); if constexpr (tiny-range<Pattern>) { const auto & cur = x.i_.current; if (cur == end) return true; if (pcur == pend) return x.incremented_; return *cur == *pcur; } else { auto cur = x.i_.current; if (cur == end) return true; if (pcur == pend) return x.incremented_; do { if (*cur != *pcur) return false; if (++pcur == pend) return true; } while (++cur != end); return false; }
friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) noexcept(noexcept(ranges::iter_swap(x.i_­.current, y.i_­.current))) requires indirectly_­swappable<iterator_t<Base>>;
Effects: Equivalent to ranges​::​iter_­swap(x.i_­.current, y.i_­.current).

24.7.14 Split view [range.split]

24.7.14.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.
The name views​::​split denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​split(E, F) is expression-equivalent to split_­view(E, F).
[Example 1: string str{"the quick brown fox"}; for (string_view word : split(str, ' ')) { cout << word << '*'; } // The above prints: the*quick*brown*fox* — end example]

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

namespace std::ranges { template<forward_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> class split_view : public view_interface<split_view<V, Pattern>> { private: V base_ = V(); // exposition only Pattern pattern_ = Pattern(); // exposition only // [range.split.iterator], class split_­view​::​iterator struct iterator; // exposition only // [range.split.sentinel], class split_­view​::​sentinel struct sentinel; // exposition only public: split_view() requires default_­initializable<V> && default_­initializable<Pattern> = default; constexpr split_view(V base, Pattern pattern); template<forward_­range R> requires constructible_­from<V, views::all_t<R>> && constructible_­from<Pattern, single_view<range_value_t<R>>> constexpr split_view(R&& r, range_value_t<R> e); constexpr V base() const& requires copyable<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr iterator begin(); constexpr auto end() { if constexpr (common_­range<V>) { return iterator{*this, ranges::end(base_), {}}; } else { return sentinel{*this}; } } constexpr subrange<iterator_t<V>> find-next(iterator_t<V>); // exposition only }; template<class R, class P> split_view(R&&, P&&) -> split_view<views::all_t<R>, views::all_t<P>>; template<forward_­range R> split_view(R&&, range_value_t<R>) -> split_view<views::all_t<R>, single_view<range_value_t<R>>>; }
constexpr split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std​::​move(base), and pattern_­ with std​::​move(pattern).
template<forward_­range R> requires constructible_­from<V, views::all_t<R>> && constructible_­from<Pattern, single_view<range_value_t<R>>> constexpr split_view(R&& r, range_value_t<R> e);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)), and pattern_­ with views​::​
single(std​::​move(e))
.
constexpr iterator begin();
Returns: {*this, ranges​::​begin(base_­), find-next(ranges​::​begin(base_­))}.
Remarks: In order to provide the amortized constant time complexity required by the range concept, this function caches the result within the split_­view for use on subsequent calls.
constexpr subrange<iterator_t<V>> find-next(iterator_t<V> it); // exposition only
Effects: Equivalent to: auto [b, e] = ranges::search(subrange(it, ranges::end(base_)), pattern_); if (b != ranges::end(base_) && ranges::empty(pattern_)) { ++b; ++e; } return {b, e};

24.7.14.3 Class split_­view​::​iterator [range.split.iterator]

namespace std::ranges { template<forward_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> class split_view<V, Pattern>::iterator { private: split_view* parent_ = nullptr; // exposition only iterator_t<V> cur_ = iterator_t<V>(); // exposition only subrange<iterator_t<V>> next_ = subrange<iterator_t<V>>(); // exposition only bool trailing_empty_ = false; // exposition only public: using iterator_concept = forward_iterator_tag; using iterator_category = input_iterator_tag; using value_type = subrange<iterator_t<V>>; using difference_type = range_difference_t<V>; iterator() = default; constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next); constexpr iterator_t<V> base() const; constexpr value_type operator*() const; constexpr iterator& operator++(); constexpr iterator operator++(int); friend constexpr bool operator==(const iterator& x, const iterator& y); }; }
constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
Effects: Initializes parent_­ with addressof(parent), cur_­ with std​::​move(current), and next_­ with std​::​move(next).
constexpr iterator_t<V> base() const;
Effects: Equivalent to return cur_­;
constexpr value_type operator*() const;
Effects: Equivalent to return {cur_­, next_­.begin()};
constexpr iterator& operator++();
Effects: Equivalent to: cur_ = next_.begin(); if (cur_ != ranges::end(parent_->base_)) { cur_ = next_.end(); if (cur_ == ranges::end(parent_->base_)) { trailing_empty_ = true; next_ = {cur_, cur_}; } else { next_ = parent_->find-next(cur_); } } else { trailing_empty_ = false; } return *this;
constexpr iterator operator++(int);
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y);
Effects: Equivalent to: return x.cur_ == y.cur_ && x.trailing_empty_ == y.trailing_empty_;

24.7.14.4 Class split_­view​::​sentinel [range.split.sentinel]

namespace std::ranges { template<forward_­range V, forward_­range Pattern> requires view<V> && view<Pattern> && indirectly_­comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> struct split_view<V, Pattern>::sentinel { private: sentinel_t<V> end_ = sentinel_t<V>(); // exposition only public: sentinel() = default; constexpr explicit sentinel(split_view& parent); friend constexpr bool operator==(const iterator& x, const sentinel& y); }; }
constexpr explicit sentinel(split_view& parent);
Effects: Initializes end_­ with ranges​::​end(parent.base_­).
friend constexpr bool operator==(const iterator& x, const sentinel& y);
Effects: Equivalent to: return x.cur_­ == y.end_­ && !x.trailing_­empty_­;

24.7.15 Counted view [range.counted]

A counted view presents a view of the elements of the counted range ([iterator.requirements.general]) for an iterator i and non-negative integer n.
The name views​::​counted denotes a customization point object ([customization.point.object]).
Let E and F be expressions, let T be decay_­t<decltype((E))>, and let D be iter_­difference_­t<T>.
If decltype((F)) does not model convertible_­to<D>, views​::​counted(E, F) is ill-formed.
[Note 1:
This case can result in substitution failure when views​::​counted(E, F) appears in the immediate context of a template instantiation.
— end note]
Otherwise, views​::​counted(E, F) is expression-equivalent to:

24.7.16 Common view [range.common]

24.7.16.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 1:
common_­view is useful for calling legacy algorithms that expect a range's iterator and sentinel types to be the same.
— end note]
The name views​::​common denotes a range adaptor object ([range.adaptor.object]).
Given a subexpression E, the expression views​::​common(E) is expression-equivalent to:
  • views​::​all(E), if decltype((E)) models common_­range and views​::​all(E) is a well-formed expression.
  • Otherwise, common_­view{E}.
[Example 1: // Legacy algorithm: template<class ForwardIterator> size_t count(ForwardIterator first, ForwardIterator last); template<forward_­range R> void my_algo(R&& r) { auto&& common = views::common(r); auto cnt = count(common.begin(), common.end()); // ... } — end example]

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

namespace std::ranges { template<view V> requires (!common_­range<V> && copyable<iterator_t<V>>) class common_view : public view_interface<common_view<V>> { private: V base_ = V(); // exposition only public: common_view() requires default_­initializable<V> = default; constexpr explicit common_view(V r); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() { if constexpr (random_­access_­range<V> && sized_­range<V>) return ranges::begin(base_); else return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::begin(base_)); } constexpr auto begin() const requires range<const V> { if constexpr (random_­access_­range<const V> && sized_­range<const V>) return ranges::begin(base_); else return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::begin(base_)); } constexpr auto end() { if constexpr (random_­access_­range<V> && sized_­range<V>) return ranges::begin(base_) + ranges::size(base_); else return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::end(base_)); } constexpr auto end() const requires range<const V> { if constexpr (random_­access_­range<const V> && sized_­range<const V>) return ranges::begin(base_) + ranges::size(base_); else return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::end(base_)); } constexpr auto size() requires sized_­range<V> { return ranges::size(base_); } constexpr auto size() const requires sized_­range<const V> { return ranges::size(base_); } }; template<class R> common_view(R&&) -> common_view<views::all_t<R>>; }
constexpr explicit common_view(V base);
Effects: Initializes base_­ with std​::​move(base).

24.7.17 Reverse view [range.reverse]

24.7.17.1 Overview [range.reverse.overview]

reverse_­view takes a bidirectional view and produces another view that iterates the same elements in reverse order.
The name views​::​reverse denotes a range adaptor object ([range.adaptor.object]).
Given a subexpression E, the expression views​::​reverse(E) is expression-equivalent to:
  • If the type of E is a (possibly cv-qualified) specialization of reverse_­view, equivalent to E.base().
  • Otherwise, if the type of E is cv 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}.
[Example 1: vector<int> is {0,1,2,3,4}; for (int i : is | views::reverse) cout << i << ' '; // prints: 4 3 2 1 0 — end example]

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

namespace std::ranges { template<view V> requires bidirectional_­range<V> class reverse_view : public view_interface<reverse_view<V>> { private: V base_ = V(); // exposition only public: reverse_view() requires default_­initializable<V> = default; constexpr explicit reverse_view(V r); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr reverse_iterator<iterator_t<V>> begin(); constexpr reverse_iterator<iterator_t<V>> begin() requires common_­range<V>; constexpr auto begin() const requires common_­range<const V>; constexpr reverse_iterator<iterator_t<V>> end(); constexpr auto end() const requires common_­range<const V>; constexpr auto size() requires sized_­range<V> { return ranges::size(base_); } constexpr auto size() const requires sized_­range<const V> { return ranges::size(base_); } }; template<class R> reverse_view(R&&) -> reverse_view<views::all_t<R>>; }
constexpr explicit reverse_view(V base);
Effects: Initializes base_­ with std​::​move(base).
constexpr reverse_iterator<iterator_t<V>> begin();
Returns: make_reverse_iterator(ranges::next(ranges::begin(base_), ranges::end(base_)))
Remarks: In order to provide the amortized constant time complexity required by the range concept, this function caches the result within the reverse_­view for use on subsequent calls.
constexpr reverse_iterator<iterator_t<V>> begin() requires common_­range<V>; constexpr auto begin() const requires common_­range<const V>;
Effects: Equivalent to: return make_­reverse_­iterator(ranges​::​end(base_­));
constexpr reverse_iterator<iterator_t<V>> end(); constexpr auto end() const requires common_­range<const V>;
Effects: Equivalent to: return make_­reverse_­iterator(ranges​::​begin(base_­));

24.7.18 Elements view [range.elements]

24.7.18.1 Overview [range.elements.overview]

elements_­view takes a view of tuple-like values and a size_­t, and produces a view with a value-type of the element of the adapted view's value-type.
The name views​::​elements<N> denotes a range adaptor object ([range.adaptor.object]).
Given a subexpression E and constant expression N, the expression views​::​elements<N>(E) is expression-equivalent to elements_­view<views​::​all_­t<decltype((E))>, N>{E}.
[Example 1: auto historical_figures = map{ pair{"Lovelace"sv, 1815}, {"Turing"sv, 1912}, {"Babbage"sv, 1791}, {"Hamilton"sv, 1936} }; auto names = historical_figures | views::elements<0>; for (auto&& name : names) { cout << name << ' '; // prints Babbage Hamilton Lovelace Turing } auto birth_years = historical_figures | views::elements<1>; for (auto&& born : birth_years) { cout << born << ' '; // prints 1791 1936 1815 1912 } — end example]
keys_­view is an alias for elements_­view<views​::​all_­t<R>, 0>, and is useful for extracting keys from associative containers.
[Example 2: auto names = historical_figures | views::keys; for (auto&& name : names) { cout << name << ' '; // prints Babbage Hamilton Lovelace Turing } — end example]
values_­view is an alias for elements_­view<views​::​all_­t<R>, 1>, and is useful for extracting values from associative containers.
[Example 3: auto is_even = [](const auto x) { return x % 2 == 0; }; cout << ranges::count_if(historical_figures | views::values, is_even); // prints 2 — end example]

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

namespace std::ranges { template<class T, size_t N> concept has-tuple-element = // exposition only requires(T t) { typename tuple_size<T>::type; requires N < tuple_size_v<T>; typename tuple_element_t<N, T>; { std::get<N>(t) } -> convertible_­to<const tuple_element_t<N, T>&>; }; template<class T, size_t N> concept returnable-element = // exposition only is_reference_v<T> || move_constructible<tuple_element_t<N, T>>; template<input_­range V, size_t N> requires view<V> && has-tuple-element<range_value_t<V>, N> && has-tuple-element<remove_reference_t<range_reference_t<V>>, N> && returnable-element<range_reference_t<V>, N> class elements_view : public view_interface<elements_view<V, N>> { public: elements_view() requires default_­initializable<V> = default; constexpr explicit elements_view(V base); constexpr V base() const& requires copy_­constructible<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() requires (!simple-view<V>) { return iterator<false>(ranges::begin(base_)); } constexpr auto begin() const requires range<const V> { return iterator<true>(ranges::begin(base_)); } constexpr auto end() requires (!simple-view<V> && !common_­range<V>) { return sentinel<false>{ranges::end(base_)}; } constexpr auto end() requires (!simple-view<V> && common_­range<V>) { return iterator<false>{ranges::end(base_)}; } constexpr auto end() const requires range<const V> { return sentinel<true>{ranges::end(base_)}; } constexpr auto end() const requires common_­range<const V> { return iterator<true>{ranges::end(base_)}; } constexpr auto size() requires sized_­range<V> { return ranges::size(base_); } constexpr auto size() const requires sized_­range<const V> { return ranges::size(base_); } private: // [range.elements.iterator], class template elements_­view​::​iterator template<bool> struct iterator; // exposition only // [range.elements.sentinel], class template elements_­view​::​sentinel template<bool> struct sentinel; // exposition only V base_ = V(); // exposition only }; }
constexpr explicit elements_view(V base);
Effects: Initializes base_­ with std​::​move(base).

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

namespace std::ranges { template<input_­range V, size_t N> requires view<V> && has-tuple-element<range_value_t<V>, N> && has-tuple-element<remove_reference_t<range_reference_t<V>>, N> && returnable-element<range_reference_t<V>, N> template<bool Const> class elements_view<V, N>::iterator { // exposition only using Base = maybe-const<Const, V>; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only static constexpr decltype(auto) get-element(const iterator_t<Base>& i); // exposition only public: using iterator_concept = see below; using iterator_category = see below; // not always present using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>; using difference_type = range_difference_t<Base>; iterator() requires default_­initializable<iterator_t<Base>> = default; constexpr explicit iterator(iterator_t<Base> current); constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>; constexpr const iterator_t<Base>& base() const&; constexpr iterator_t<Base> base() &&; constexpr decltype(auto) operator*() const { return get-element(current_); } constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires forward_­range<Base>; constexpr iterator& operator--() requires bidirectional_­range<Base>; constexpr iterator operator--(int) requires bidirectional_­range<Base>; constexpr iterator& operator+=(difference_type x) requires random_­access_­range<Base>; constexpr iterator& operator-=(difference_type x) requires random_­access_­range<Base>; constexpr decltype(auto) operator[](difference_type n) const requires random_­access_­range<Base> { return get-element(current_ + n); } friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<Base>>; friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_­access_­range<Base>; friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires random_­access_­range<Base> && three_­way_­comparable<iterator_t<Base>>; friend constexpr iterator operator+(const iterator& x, difference_type y) requires random_­access_­range<Base>; friend constexpr iterator operator+(difference_type x, const iterator& y) requires random_­access_­range<Base>; friend constexpr iterator operator-(const iterator& x, difference_type y) requires random_­access_­range<Base>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires sized_­sentinel_­for<iterator_t<Base>, iterator_t<Base>>; }; }
The member typedef-name iterator_­concept is defined as follows:
The member typedef-name iterator_­category is defined if and only if Base models forward_­range.
In that case, iterator_­category is defined as follows: Let C denote the type iterator_­traits<iterator_­t<Base>>​::​iterator_­category.
  • If std​::​get<N>(*current_­) is an rvalue, iterator_­category denotes input_­iterator_­tag.
  • Otherwise, if C models derived_­from<random_­access_­iterator_­tag>, iterator_­category denotes random_­access_­iterator_­tag.
  • Otherwise, iterator_­category denotes C.
static constexpr decltype(auto) get-element(const iterator_t<Base>& i); // exposition only
Effects: Equivalent to: if constexpr (is_reference_v<range_reference_t<Base>>) { return std::get<N>(*i); } else { using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>; return static_cast<E>(std::get<N>(*i)); }
constexpr explicit iterator(iterator_t<Base> current);
Effects: Initializes current_­ with std​::​move(current).
constexpr iterator(iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>;
Effects: Initializes current_­ with std​::​move(i.current_­).
constexpr const iterator_t<Base>& base() const&;
Effects: Equivalent to: return current_­;
constexpr iterator_t<Base> base() &&;
Effects: Equivalent to: return std​::​move(current_­);
constexpr iterator& operator++();
Effects: Equivalent to: ++current_; return *this;
constexpr void operator++(int);
Effects: Equivalent to: ++current_­.
constexpr iterator operator++(int) requires forward_­range<Base>;
Effects: Equivalent to: auto temp = *this; ++current_; return temp;
constexpr iterator& operator--() requires bidirectional_­range<Base>;
Effects: Equivalent to: --current_; return *this;
constexpr iterator operator--(int) requires bidirectional_­range<Base>;
Effects: Equivalent to: auto temp = *this; --current_; return temp;
constexpr iterator& operator+=(difference_type n); requires random_­access_­range<Base>;
Effects: Equivalent to: current_ += n; return *this;
constexpr iterator& operator-=(difference_type n) requires random_­access_­range<Base>;
Effects: Equivalent to: current_ -= n; return *this;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<Base>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator<(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return x.current_­ < y.current_­;
friend constexpr bool operator>(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return y < x;
friend constexpr bool operator<=(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return !(y < x);
friend constexpr bool operator>=(const iterator& x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return !(x < y);
friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires random_­access_­range<Base> && three_­way_­comparable<iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ <=> y.current_­;
friend constexpr iterator operator+(const iterator& x, difference_type y) requires random_­access_­range<Base>;
Effects: Equivalent to: return iterator{x} += y;
friend constexpr iterator operator+(difference_type x, const iterator& y) requires random_­access_­range<Base>;
Effects: Equivalent to: return y + x;
friend constexpr iterator operator-(const iterator& x, difference_type y) requires random_­access_­range<Base>;
Effects: Equivalent to: return iterator{x} -= y;
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires sized_­sentinel_­for<iterator_t<Base>, iterator_t<Base>>;
Effects: Equivalent to: return x.current_­ - y.current_­;

24.7.18.4 Class template elements_­view​::​sentinel [range.elements.sentinel]

namespace std::ranges { template<input_­range V, size_t N> requires view<V> && has-tuple-element<range_value_t<V>, N> && has-tuple-element<remove_reference_t<range_reference_t<V>>, N> && returnable-element<range_reference_t<V>, N> template<bool Const> class elements_view<V, N>::sentinel { // exposition only private: using Base = maybe-const<Const, 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> other) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& x, const iterator<OtherConst>& y); }; }
constexpr explicit sentinel(sentinel_t<Base> end);
Effects: Initializes end_­ with end.
constexpr sentinel(sentinel<!Const> other) requires Const && convertible_­to<sentinel_t<V>, sentinel_t<Base>>;
Effects: Initializes end_­ with std​::​move(other.end_­).
constexpr sentinel_t<Base> base() const;
Effects: Equivalent to: return end_­;
template<bool OtherConst> requires sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ == y.end_­;
template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y);
Effects: Equivalent to: return x.current_­ - y.end_­;
template<bool OtherConst> requires sized_­sentinel_­for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& x, const iterator<OtherConst>& y);
Effects: Equivalent to: return x.end_­ - y.current_­;