26 Ranges library [ranges]

26.7 Range adaptors [range.adaptors]

26.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]

26.7.2 Range adaptor objects [range.adaptor.object]

A range adaptor closure object is a unary function object that accepts a range argument.
For a range adaptor closure object C and an expression R such that decltype((R)) models range, the following expressions are equivalent: 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.
Given an object t of type T, where
  • t is a unary function object that accepts a range argument,
  • T models derived_from<range_adaptor_closure<T>>,
  • T has no other base classes of type range_adaptor_closure<U> for any other type U, and
  • T does not model range
then the implementation ensures that t is a range adaptor closure object.
The template parameter D for range_adaptor_closure may be an incomplete type.
If an expression of type cv D is used as an operand to the | operator, D shall be complete and model derived_from<range_adaptor_closure<D>>.
The behavior of an expression involving an object of type cv D as an operand to the | operator is undefined if overload resolution selects a program-defined operator| function.
If an expression of type cv U is used as an operand to the | operator, where U has a base class of type range_adaptor_closure<T> for some type T other than U, the behavior is undefined.
The behavior of a program that adds a specialization for range_adaptor_closure is undefined.
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 ([func.require]) 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.

26.7.3 Movable wrapper [range.move.wrap]

Many types in this subclause are specified in terms of an exposition-only class template movable-box.
movable-box<T> behaves exactly like optional<T> with the following differences:
  • movable-box<T> constrains its type parameter T with move_constructible<T> && is_object_v<T>.
  • The default constructor of movable-box<T> is equivalent to: constexpr movable-box() noexcept(is_nothrow_default_constructible_v<T>) requires default_initializable<T> : movable-box{in_place} {}
  • If copyable<T> is not modeled, the copy assignment operator is equivalent to: constexpr movable-box& operator=(const movable-box& that) noexcept(is_nothrow_copy_constructible_v<T>) requires copy_constructible<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: constexpr movable-box& operator=(movable-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:
  • If copy_constructible<T> is true, movable-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.
  • Otherwise, movable-box<T> should store only a T if either T models movable or is_nothrow_move_constructible_v<T> is true.

26.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 direct-non-list-initializes the contained value with *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]

26.7.5 Range adaptor helpers [range.adaptor.helpers]

namespace std::ranges { template<class F, class Tuple> constexpr auto tuple-transform(F&& f, Tuple&& t) { // exposition only return apply([&]<class... Ts>(Ts&&... elements) { return tuple<invoke_result_t<F&, Ts>...>(invoke(f, std::forward<Ts>(elements))...); }, std::forward<Tuple>(t)); } template<class F, class Tuple> constexpr void tuple-for-each(F&& f, Tuple&& t) { // exposition only apply([&]<class... Ts>(Ts&&... elements) { (static_cast<void>(invoke(f, std::forward<Ts>(elements))), ...); }, std::forward<Tuple>(t)); } template<class T> constexpr T& as-lvalue(T&& t) { // exposition only return static_cast<T&>(t); } template<bool Const, class... Views> concept all-random-access = // exposition only (random_access_range<maybe-const<Const, Views>> && ...); template<bool Const, class... Views> concept all-bidirectional = // exposition only (bidirectional_range<maybe-const<Const, Views>> && ...); template<bool Const, class... Views> concept all-forward = // exposition only (forward_range<maybe-const<Const, Views>> && ...); }

26.7.6 All view [range.all]

26.7.6.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, owning_view{E}.

26.7.6.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>()); }

26.7.6.3 Class template owning_view [range.owning.view]

owning_view is a move-only view of the elements of some other range.
namespace std::ranges { template<range R> requires movable<R> && (!is-initializer-list<R>) // see [range.refinements] class owning_view : public view_interface<owning_view<R>> { private: R r_ = R(); // exposition only public: owning_view() requires default_initializable<R> = default; constexpr owning_view(R&& t); owning_view(owning_view&&) = default; owning_view& operator=(owning_view&&) = default; constexpr R& base() & noexcept { return r_; } constexpr const R& base() const & noexcept { return r_; } constexpr R&& base() && noexcept { return std::move(r_); } constexpr const R&& base() const && noexcept { return std::move(r_); } constexpr iterator_t<R> begin() { return ranges::begin(r_); } constexpr sentinel_t<R> end() { return ranges::end(r_); } constexpr auto begin() const requires range<const R> { return ranges::begin(r_); } constexpr auto end() const requires range<const R> { return ranges::end(r_); } constexpr bool empty() requires requires { ranges::empty(r_); } { return ranges::empty(r_); } constexpr bool empty() const requires requires { ranges::empty(r_); } { return ranges::empty(r_); } constexpr auto size() requires sized_range<R> { return ranges::size(r_); } constexpr auto size() const requires sized_range<const R> { return ranges::size(r_); } constexpr auto data() requires contiguous_range<R> { return ranges::data(r_); } constexpr auto data() const requires contiguous_range<const R> { return ranges::data(r_); } }; }
constexpr owning_view(R&& t);
Effects: Initializes r_ with std​::​move(t).

26.7.7 As rvalue view [range.as.rvalue]

26.7.7.1 Overview [range.as.rvalue.overview]

as_rvalue_view presents a view of an underlying sequence with the same behavior as the underlying sequence except that its elements are rvalues.
Some generic algorithms can be called with an as_rvalue_view to replace copying with moving.
The name views​::​as_rvalue denotes a range adaptor object ([range.adaptor.object]).
Let E be an expression and let T be decltype((E)).
The expression views​::​as_rvalue(E) is expression-equivalent to:
  • views​::​all(E) if same_as<range_rvalue_reference_t<T>, range_reference_t<T>> is true.
  • Otherwise, as_rvalue_view(E).
[Example 1: vector<string> words = {"the", "quick", "brown", "fox", "ate", "a", "pterodactyl"}; vector<string> new_words; ranges::copy(words | views::as_rvalue, back_inserter(new_words)); // moves each string from words into new_words — end example]

26.7.7.2 Class template as_rvalue_view [range.as.rvalue.view]

namespace std::ranges { template<view V> requires input_range<V> class as_rvalue_view : public view_interface<as_rvalue_view<V>> { V base_ = V(); // exposition only public: as_rvalue_view() requires default_initializable<V> = default; constexpr explicit as_rvalue_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 move_iterator(ranges::begin(base_)); } constexpr auto begin() const requires range<const V> { return move_iterator(ranges::begin(base_)); } constexpr auto end() requires (!simple-view<V>) { if constexpr (common_range<V>) { return move_iterator(ranges::end(base_)); } else { return move_sentinel(ranges::end(base_)); } } constexpr auto end() const requires range<const V> { if constexpr (common_range<const V>) { return move_iterator(ranges::end(base_)); } else { return move_sentinel(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> as_rvalue_view(R&&) -> as_rvalue_view<views::all_t<R>>; }
constexpr explicit as_rvalue_view(V base);
Effects: Initializes base_ with std​::​move(base).

26.7.8 Filter view [range.filter]

26.7.8.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]

26.7.8.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 movable-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 explicit 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 explicit 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.

26.7.8.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 & noexcept; 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:
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 & noexcept;
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_).

26.7.8.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_;

26.7.9 Transform view [range.transform]

26.7.9.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]

26.7.9.2 Class template transform_view [range.transform.view]

namespace std::ranges { template<input_range V, move_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 movable-box<F> fun_; // exposition only public: transform_view() requires default_initializable<V> && default_initializable<F> = default; constexpr explicit 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 explicit 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_)};

26.7.9.3 Class template transform_view​::​iterator [range.transform.iterator]

namespace std::ranges { template<input_range V, move_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<maybe-const<Const, 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 & noexcept; constexpr iterator_t<Base> base() &&; constexpr decltype(auto) operator*() const noexcept(noexcept(invoke(*parent_->fun_, *current_))) { 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>>; }; }
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_reference_v<invoke_result_t<maybe-const<Const, 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 & noexcept;
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_;

26.7.9.4 Class template transform_view​::​sentinel [range.transform.sentinel]

namespace std::ranges { template<input_range V, move_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_;

26.7.10 Take view [range.take]

26.7.10.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:
  • If T is a specialization of empty_view ([range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.
  • Otherwise, if T models random_access_range and sized_range and is a specialization of span ([views.span]), basic_string_view ([string.view]), or subrange ([range.subrange]), then U(ranges​::​begin(E), ranges​::​begin(E) + std​::​min<D>(ranges​::​distance(E), F)), except that E is evaluated only once, where U is a type determined as follows:
    • if T is a specialization of span, then U is span<typename T​::​element_type>;
    • otherwise, if T is a specialization of basic_string_view, then U is T;
    • otherwise, T is a specialization of subrange, and U is subrange<iterator_t<T>>;
  • otherwise, if T is a specialization of iota_view ([range.iota.view]) that models random_access_range and sized_range, then iota_view(*ranges​::​begin(E), *(ranges​::​begin(E) + std​::​
    min<D>(ranges​::​distance(E), F)))
    , except that E is evaluated only once.
  • Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):
    • if T models sized_range, then views::repeat(*E.value_, std::min<D>(ranges::distance(E), F)) except that E is evaluated only once;
    • otherwise, views​::​repeat(*E.value_, static_cast<D>(F)).
  • Otherwise, take_view(E, F).
[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]

26.7.10.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> class sentinel; // exposition only public: take_view() requires default_initializable<V> = default; constexpr explicit 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 = range_difference_t<V>(size()); return counted_iterator(ranges::begin(base_), sz); } } else if constexpr (sized_sentinel_for<sentinel_t<V>, iterator_t<V>>) { auto it = ranges::begin(base_); auto sz = std::min(count_, ranges::end(base_) - it); return counted_iterator(std::move(it), 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 = range_difference_t<const V>(size()); return counted_iterator(ranges::begin(base_), sz); } } else if constexpr (sized_sentinel_for<sentinel_t<const V>, iterator_t<const V>>) { auto it = ranges::begin(base_); auto sz = std::min(count_, ranges::end(base_) - it); return counted_iterator(std::move(it), 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_) + range_difference_t<V>(size()); else return default_sentinel; } else if constexpr (sized_sentinel_for<sentinel_t<V>, iterator_t<V>>) { 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_) + range_difference_t<const V>(size()); else return default_sentinel; } else if constexpr (sized_sentinel_for<sentinel_t<const V>, iterator_t<const V>>) { 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 explicit take_view(V base, range_difference_t<V> count);
Preconditions: count >= 0 is true.
Effects: Initializes base_ with std​::​move(base) and count_ with count.

26.7.10.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_;

26.7.11 Take while view [range.take.while]

26.7.11.1 Overview [range.take.while.overview]

Given a unary predicate pred and a view r, take_while_view produces a view of the range [ranges​::​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 = views::istream<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]

26.7.11.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 movable-box<Pred> pred_; // exposition only public: take_while_view() requires default_initializable<V> && default_initializable<Pred> = default; constexpr explicit 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 explicit 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_;

26.7.11.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 { 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 std​::​move(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);

26.7.12 Drop view [range.drop]

26.7.12.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:
  • If T is a specialization of empty_view ([range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.
  • Otherwise, if T models random_access_range and sized_range and is then U(ranges​::​begin(E) + std​::​min<D>(ranges​::​distance(E), F), ranges​::​end(E)), except that E is evaluated only once, where U is span<typename T​::​element_type> if T is a specialization of span and T otherwise.
  • Otherwise, if T is a specialization of subrange that models random_access_range and sized_range, then T(ranges​::​begin(E) + std​::​min<D>(ranges​::​distance(E), F), ranges​::​
    end(E), to-unsigned-like(ranges​::​distance(E) - std​::​min<D>(ranges​::​distance(E), F)))
    , except that E and F are each evaluated only once.
  • Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):
    • if T models sized_range, then views::repeat(*E.value_, ranges::distance(E) - std::min<D>(ranges::distance(E), F)) except that E is evaluated only once;
    • otherwise, ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.
  • Otherwise, drop_view(E, F).
[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]

26.7.12.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 explicit 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 explicit 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]

26.7.13 Drop while view [range.drop.while]

26.7.13.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"sv; 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]

26.7.13.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 explicit 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 movable-box<Pred> pred_; // exposition only }; template<class R, class Pred> drop_while_view(R&&, Pred) -> drop_while_view<views::all_t<R>, Pred>; }
constexpr explicit 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]

26.7.14 Join view [range.join]

26.7.14.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]

26.7.14.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<iterator_t<V>> outer_; // exposition only, present only // when !forward_range<V> non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only // if is_reference_v<InnerRng> is false 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() { if constexpr (forward_range<V>) { constexpr bool use_const = simple-view<V> && is_reference_v<InnerRng>; return iterator<use_const>{*this, ranges::begin(base_)}; } else { outer_ = ranges::begin(base_); return iterator<false>{*this}; } } constexpr auto begin() const requires forward_range<const V> && is_reference_v<range_reference_t<const V>> && input_range<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 forward_range<const V> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> { if constexpr (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).

26.7.14.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, present only // if Base models forward_range optional<InnerIter> inner_; // exposition only Parent* parent_ = nullptr; // exposition only constexpr void satisfy(); // exposition only constexpr OuterIter& outer(); // exposition only constexpr const OuterIter& outer() const; // exposition only constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<Base>; // exposition only constexpr explicit iterator(Parent& parent) requires (!forward_range<Base>); // 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() = default; 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 && forward_range<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, Base models bidirectional_range, and range_reference_t<Base> models both bidirectional_range and common_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> and range_reference_t<Base> models common_range, 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 OuterIter& outer(); constexpr const OuterIter& outer() const;
Returns: outer_ if Base models forward_range; otherwise, *parent_->outer_.
constexpr void satisfy();
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_.reset();
constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<Base>;
Effects: Initializes outer_ with std​::​move(outer) and parent_ with addressof(parent); then calls satisfy().
constexpr explicit iterator(Parent& parent) requires (!forward_range<Base>);
Effects: Initializes 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_.
[Note 1: 
Const can only be true when Base models forward_range.
— end note]
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: if (++*inner_ == ranges::end(as-lvalue(inner-range))) { ++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(as-lvalue(*--outer_)); while (*inner_ == ranges::begin(as-lvalue(*outer_))) *inner_ = ranges::end(as-lvalue(*--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 && forward_range<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_);

26.7.14.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_;

26.7.15 Join with view [range.join.with]

26.7.15.1 Overview [range.join.with.overview]

join_with_view takes a view and a delimiter, and flattens the view, inserting every element of the delimiter in between elements of the view.
The delimiter can be a single element or a view of elements.
The name views​::​join_with denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​join_with(E, F) is expression-equivalent to join_with_view(E, F).
[Example 1: vector<string> vs = {"the", "quick", "brown", "fox"}; for (char c : vs | views::join_with('-')) { cout << c; } // The above prints the-quick-brown-fox — end example]

26.7.15.2 Class template join_with_view [range.join.with.view]

namespace std::ranges { template<class R, class P> concept compatible-joinable-ranges = // exposition only common_with<range_value_t<R>, range_value_t<P>> && common_reference_with<range_reference_t<R>, range_reference_t<P>> && common_reference_with<range_rvalue_reference_t<R>, range_rvalue_reference_t<P>>; template<class R> concept bidirectional-common = bidirectional_range<R> && common_range<R>; // exposition only template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern> class join_with_view : public view_interface<join_with_view<V, Pattern>> { using InnerRng = range_reference_t<V>; // exposition only V base_ = V(); // exposition only non-propagating-cache<iterator_t<V>> outer_it_; // exposition only, present only // when !forward_range<V> non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only // if is_reference_v<InnerRng> is false Pattern pattern_ = Pattern(); // exposition only // [range.join.with.iterator], class template join_with_view​::​iterator template<bool Const> struct iterator; // exposition only // [range.join.with.sentinel], class template join_with_view​::​sentinel template<bool Const> struct sentinel; // exposition only public: join_with_view() requires default_initializable<V> && default_initializable<Pattern> = default; constexpr explicit join_with_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<InnerRng>>> constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> 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>) { constexpr bool use_const = simple-view<V> && is_reference_v<InnerRng> && simple-view<Pattern>; return iterator<use_const>{*this, ranges::begin(base_)}; } else { outer_it_ = ranges::begin(base_); return iterator<false>{*this}; } } constexpr auto begin() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<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> && simple-view<Pattern>>{*this, ranges::end(base_)}; else return sentinel<simple-view<V> && simple-view<Pattern>>{*this}; } constexpr auto end() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> { using InnerConstRng = range_reference_t<const V>; if constexpr (forward_range<InnerConstRng> && common_range<const V> && common_range<InnerConstRng>) return iterator<true>{*this, ranges::end(base_)}; else return sentinel<true>{*this}; } }; template<class R, class P> join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>; template<input_range R> join_with_view(R&&, range_value_t<range_reference_t<R>>) -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>; }
constexpr explicit join_with_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<InnerRng>>> constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
Effects: Initializes base_ with views​::​all(std​::​forward<R>(r)) and pattern_ with views​::​single(std​::​move(e)).

26.7.15.3 Class template join_with_view​::​iterator [range.join.with.iterator]

namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::iterator { using Parent = maybe-const<Const, join_with_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only using InnerBase = range_reference_t<Base>; // exposition only using PatternBase = maybe-const<Const, Pattern>; // exposition only using OuterIter = iterator_t<Base>; // exposition only using InnerIter = iterator_t<InnerBase>; // exposition only using PatternIter = iterator_t<PatternBase>; // exposition only static constexpr bool ref-is-glvalue = is_reference_v<InnerBase>; // exposition only Parent* parent_ = nullptr; // exposition only OuterIter outer_it_ = OuterIter(); // exposition only, present only // if Base models forward_range variant<PatternIter, InnerIter> inner_it_; // exposition only constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<Base>; // exposition only constexpr explicit iterator(Parent& parent) requires (!forward_range<Base>); // exposition only constexpr OuterIter& outer(); // exposition only constexpr const OuterIter& outer() const; // exposition only constexpr auto& update-inner(); // exposition only constexpr auto& get-inner(); // exposition only constexpr void satisfy(); // exposition only public: using iterator_concept = see below; using iterator_category = see below; // not always present using value_type = see below; using difference_type = see below; iterator() = default; constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, OuterIter> && convertible_to<iterator_t<InnerRng>, InnerIter> && convertible_to<iterator_t<Pattern>, PatternIter>; constexpr decltype(auto) operator*() const; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>; constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>; friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && forward_range<Base> && equality_comparable<InnerIter>; friend constexpr decltype(auto) iter_move(const iterator& x) { using rvalue_reference = common_reference_t< iter_rvalue_reference_t<InnerIter>, iter_rvalue_reference_t<PatternIter>>; return visit<rvalue_reference>(ranges::iter_move, x.inner_it_); } friend constexpr void iter_swap(const iterator& x, const iterator& y) requires indirectly_swappable<InnerIter, PatternIter> { visit(ranges::iter_swap, x.inner_it_, y.inner_it_); } }; }
iterator​::​iterator_concept is defined as follows:
  • If ref-is-glvalue is true, Base models bidirectional_range, and InnerBase and PatternBase each model bidirectional-common, then iterator_concept denotes bidirectional_iterator_tag.
  • Otherwise, if ref-is-glvalue is true and Base and InnerBase 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, and Base and InnerBase each model forward_range.
In that case, iterator​::​iterator_category is defined as follows:
  • Let OUTERC denote iterator_traits<OuterIter>​::​iterator_category, let INNERC denote iterator_traits<InnerIter>​::​iterator_category, and let PATTERNC denote iterator_-
    traits<PatternIter>​::​iterator_category
    .
  • If is_reference_v<common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>> is false, iterator_category denotes input_iterator_tag.
  • Otherwise, if OUTERC, INNERC, and PATTERNC each model derived_from<bidirectional_iterator_category> and InnerBase and PatternBase each model common_range, iterator_category denotes bidirectional_iterator_tag.
  • Otherwise, if OUTERC, INNERC, and PATTERNC each model derived_from<forward_iterator_tag>, iterator_category denotes forward_iterator_tag.
  • Otherwise, iterator_category denotes input_iterator_tag.
iterator​::​value_type denotes the type: common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
iterator​::​difference_type denotes the type: common_type_t< iter_difference_t<OuterIter>, iter_difference_t<InnerIter>, iter_difference_t<PatternIter>>
constexpr OuterIter& outer(); constexpr const OuterIter& outer() const;
Returns: outer_it_ if Base models forward_range; otherwise, *parent_->outer_it_.
constexpr auto& update-inner();
Effects: Equivalent to: if constexpr (ref-is-glvalue) return as-lvalue(*outer()); else return parent_->inner_.emplace-deref(outer());
constexpr auto& get-inner();
Effects: Equivalent to: if constexpr (ref-is-glvalue) return as-lvalue(*outer()); else return *parent_->inner_;
constexpr void satisfy();
Effects: Equivalent to: while (true) { if (inner_it_.index() == 0) { if (std::get<0>(inner_it_) != ranges::end(parent_->pattern_)) break; inner_it_.template emplace<1>(ranges::begin(update-inner())); } else { if (std::get<1>(inner_it_) != ranges::end(get-inner())) break; if (++outer() == ranges::end(parent_->base_)) { if constexpr (ref-is-glvalue) inner_it_.template emplace<0>(); break; } inner_it_.template emplace<0>(ranges::begin(parent_->pattern_)); } }
[Note 1: 
join_with_view iterators use the satisfy function to skip over empty inner ranges.
— end note]
constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<Base>; constexpr explicit iterator(Parent& parent) requires (!forward_range<Base>);
Effects: Initializes parent_ with addressof(parent).
For the first overload, also initializes outer_it_ with std​::​move(outer).
Then, equivalent to: if (outer() != ranges::end(parent_->base_)) { inner_it_.template emplace<1>(ranges::begin(update-inner())); satisfy(); }
constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, OuterIter> && convertible_to<iterator_t<InnerRng>, InnerIter> && convertible_to<iterator_t<Pattern>, PatternIter>;
Effects: Initializes outer_it_ with std​::​move(i.outer_it_) and parent_ with i.parent_.
Then, equivalent to: if (i.inner_it_.index() == 0) inner_it_.template emplace<0>(std::get<0>(std::move(i.inner_it_))); else inner_it_.template emplace<1>(std::get<1>(std::move(i.inner_it_)));
[Note 2: 
Const can only be true when Base models forward_range.
— end note]
constexpr decltype(auto) operator*() const;
Effects: Equivalent to: using reference = common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>; return visit([](auto& it) -> reference { return *it; }, inner_it_);
constexpr iterator& operator++();
Effects: Equivalent to: visit([](auto& it){ ++it; }, inner_it_); satisfy(); return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
constexpr iterator operator++(int) requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>;
Effects: Equivalent to: iterator tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
Effects: Equivalent to: if (outer_it_ == ranges::end(parent_->base_)) { auto&& inner = *--outer_it_; inner_it_.template emplace<1>(ranges::end(inner)); } while (true) { if (inner_it_.index() == 0) { auto& it = std::get<0>(inner_it_); if (it == ranges::begin(parent_->pattern_)) { auto&& inner = *--outer_it_; inner_it_.template emplace<1>(ranges::end(inner)); } else { break; } } else { auto& it = std::get<1>(inner_it_); auto&& inner = *outer_it_; if (it == ranges::begin(inner)) { inner_it_.template emplace<0>(ranges::end(parent_->pattern_)); } else { break; } } } visit([](auto& it){ --it; }, inner_it_); return *this;
constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
Effects: Equivalent to: iterator tmp = *this; --*this; return tmp;
friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && forward_range<Base> && equality_comparable<InnerIter>;
Effects: Equivalent to: return x.outer_it_ == y.outer_it_ && x.inner_it_ == y.inner_it_;

26.7.15.4 Class template join_with_view​::​sentinel [range.join.with.sentinel]

namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::sentinel { using Parent = maybe-const<Const, join_with_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only constexpr explicit sentinel(Parent& parent); // exposition only public: sentinel() = default; 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_;

26.7.16 Lazy split view [range.lazy.split]

26.7.16.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]

26.7.16.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> is false // [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 explicit 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 explicit 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> && simple-view<Pattern>> {*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> && simple-view<Pattern>> {*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 explicit 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 explicit 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))
.

26.7.16.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_, current_ with std​::​move(i.current_), and trailing_empty_ with i.trailing_empty_.
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_;

26.7.16.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 constexpr explicit value_type(outer-iterator i); // exposition only public: constexpr inner-iterator<Const> begin() const; constexpr default_sentinel_t end() const noexcept; }; }
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 noexcept;
Effects: Equivalent to: return default_sentinel;

26.7.16.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 & noexcept; constexpr iterator_t<Base> base() && requires forward_range<V>; 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 & noexcept;
Effects: Equivalent to: return i_.current;
constexpr iterator_t<Base> base() && requires forward_range<V>;
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).

26.7.17 Split view [range.split]

26.7.17.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 (auto word : views::split(str, ' ')) { cout << string_view(word) << '*'; } // The above prints the*quick*brown*fox* — end example]

26.7.17.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 explicit 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 explicit 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 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 explicit 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 explicit 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);
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};

26.7.17.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_;

26.7.17.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_;

26.7.18 Concat view [range.concat]

26.7.18.1 Overview [range.concat.overview]

concat_view presents a view that concatenates all the underlying ranges.
The name views​::​concat denotes a customization point object ([customization.point.object]).
Given a pack of subexpressions Es..., the expression views​::​concat(Es...) is expression-equivalent to
  • views​::​all(Es...) if Es is a pack with only one element,
  • otherwise, concat_view(Es...).
[Example 1: vector<int> v1{1, 2, 3}, v2{4, 5}, v3{}; array a{6, 7, 8}; auto s = views::single(9); for (auto&& i : views::concat(v1, v2, v3, a, s)) { print("{} ", i); // prints 1 2 3 4 5 6 7 8 9 } — end example]

26.7.18.2 Class template concat_view [range.concat.view]

namespace std::ranges { template<class... Rs> using concat-reference-t = common_reference_t<range_reference_t<Rs>...>; // exposition only template<class... Rs> using concat-value-t = common_type_t<range_value_t<Rs>...>; // exposition only template<class... Rs> using concat-rvalue-reference-t = // exposition only common_reference_t<range_rvalue_reference_t<Rs>...>; template<class... Rs> concept concat-indirectly-readable = see below; // exposition only template<class... Rs> concept concatable = see below; // exposition only template<bool Const, class... Rs> concept concat-is-random-access = see below; // exposition only template<bool Const, class... Rs> concept concat-is-bidirectional = see below; // exposition only template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && concatable<Views...> class concat_view : public view_interface<concat_view<Views...>> { tuple<Views...> views_; // exposition only // [range.concat.iterator], class template concat_view​::​iterator template<bool> class iterator; // exposition only public: constexpr concat_view() = default; constexpr explicit concat_view(Views... views); constexpr iterator<false> begin() requires (!(simple-view<Views> && ...)); constexpr iterator<true> begin() const requires (range<const Views> && ...) && concatable<const Views...>; constexpr auto end() requires (!(simple-view<Views> && ...)); constexpr auto end() const requires (range<const Views> && ...) && concatable<const Views...>; constexpr auto size() requires (sized_range<Views> && ...); constexpr auto size() const requires (sized_range<const Views> && ...); }; template<class... R> concat_view(R&&...) -> concat_view<views::all_t<R>...>; }
template<class... Rs> concept concat-indirectly-readable = see below; // exposition only
The exposition-only concat-indirectly-readable concept is equivalent to: template<class Ref, class RRef, class It> concept concat-indirectly-readable-impl = // exposition only requires (const It it) { { *it } -> convertible_to<Ref>; { ranges::iter_move(it) } -> convertible_to<RRef>; }; template<class... Rs> concept concat-indirectly-readable = // exposition only common_reference_with<concat-reference-t<Rs...>&&, concat-value-t<Rs...>&> && common_reference_with<concat-reference-t<Rs...>&&, concat-rvalue-reference-t<Rs...>&&> && common_reference_with<concat-rvalue-reference-t<Rs...>&&, concat-value-t<Rs...> const&> && (concat-indirectly-readable-impl<concat-reference-t<Rs...>, concat-rvalue-reference-t<Rs...>, iterator_t<Rs>> && ...);
template<class... Rs> concept concatable = see below; // exposition only
The exposition-only concatable concept is equivalent to: template<class... Rs> concept concatable = requires { // exposition only typename concat-reference-t<Rs...>; typename concat-value-t<Rs...>; typename concat-rvalue-reference-t<Rs...>; } && concat-indirectly-readable<Rs...>;
template<bool Const, class... Rs> concept concat-is-random-access = see below; // exposition only
Let Fs be the pack that consists of all elements of Rs except the last element, then concat-is-random-access is equivalent to: template<bool Const, class... Rs> concept concat-is-random-access = // exposition only all-random-access<Const, Rs...> && (common_range<maybe-const<Const, Fs>> && ...);
template<bool Const, class... Rs> concept concat-is-bidirectional = see below; // exposition only
Let Fs be the pack that consists of all elements of Rs except the last element, then concat-is-bidirectional is equivalent to: template<bool Const, class... Rs> concept concat-is-bidirectional = // exposition only all-bidirectional<Const, Rs...> && (common_range<maybe-const<Const, Fs>> && ...);
constexpr explicit concat_view(Views... views);
Effects: Initializes views_ with std​::​move(views)....
constexpr iterator<false> begin() requires (!(simple-view<Views> && ...)); constexpr iterator<true> begin() const requires (range<const Views> && ...) && concatable<const Views...>;
Effects: Let is-const be true for the const-qualified overload, and false otherwise.
Equivalent to: iterator<is-const> it(this, in_place_index<0>, ranges::begin(std::get<0>(views_))); it.template satisfy<0>(); return it;
constexpr auto end() requires (!(simple-view<Views> && ...)); constexpr auto end() const requires (range<const Views> && ...) && concatable<const Views...>;
Effects: Let is-const be true for the const-qualified overload, and false otherwise.
Equivalent to: constexpr auto N = sizeof...(Views); if constexpr (common_range<maybe-const<is-const, Views...[N - 1]>>) { return iterator<is-const>(this, in_place_index<N - 1>, ranges::end(std::get<N - 1>(views_))); } else { return default_sentinel; }
constexpr auto size() requires (sized_range<Views> && ...); constexpr auto size() const requires (sized_range<const Views> && ...);
Effects: Equivalent to: return apply( [](auto... sizes) { using CT = make-unsigned-like-t<common_type_t<decltype(sizes)...>>; return (CT(sizes) + ...); }, tuple-transform(ranges::size, views_));

26.7.18.3 Class concat_view​::​iterator [range.concat.iterator]

namespace std::ranges { template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && concatable<Views...> template<bool Const> class concat_view<Views...>::iterator { public: using iterator_category = see below; // not always present. using iterator_concept = see below; using value_type = concat-value-t<maybe-const<Const, Views>...>; using difference_type = common_type_t<range_difference_t<maybe-const<Const, Views>>...>; private: using base-iter = // exposition only variant<iterator_t<maybe-const<Const, Views>>...>; maybe-const<Const, concat_view>* parent_ = nullptr; // exposition only base-iter it_; // exposition only template<size_t N> constexpr void satisfy(); // exposition only template<size_t N> constexpr void prev(); // exposition only template<size_t N> constexpr void advance-fwd(difference_type offset, // exposition only difference_type steps); template<size_t N> constexpr void advance-bwd(difference_type offset, // exposition only difference_type steps); template<class... Args> constexpr explicit iterator(maybe-const<Const, concat_view>* parent, // exposition only Args&&... args) requires constructible_from<base-iter, Args&&...>; public: iterator() = default; constexpr iterator(iterator<!Const> i) requires Const && (convertible_to<iterator_t<Views>, iterator_t<const Views>> && ...); constexpr decltype(auto) operator*() const; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires all-forward<Const, Views...>; constexpr iterator& operator--() requires concat-is-bidirectional<Const, Views...>; constexpr iterator operator--(int) requires concat-is-bidirectional<Const, Views...>; constexpr iterator& operator+=(difference_type n) requires concat-is-random-access<Const, Views...>; constexpr iterator& operator-=(difference_type n) requires concat-is-random-access<Const, Views...>; constexpr decltype(auto) operator[](difference_type n) const requires concat-is-random-access<Const, Views...>; friend constexpr bool operator==(const iterator& x, const iterator& y) requires (equality_comparable<iterator_t<maybe-const<Const, Views>>> && ...); friend constexpr bool operator==(const iterator& it, default_sentinel_t); friend constexpr bool operator<(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator>(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator<=(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator>=(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires (all-random-access<Const, Views...> && (three_way_comparable<iterator_t<maybe-const<Const, Views>>> && ...)); friend constexpr iterator operator+(const iterator& it, difference_type n) requires concat-is-random-access<Const, Views...>; friend constexpr iterator operator+(difference_type n, const iterator& it) requires concat-is-random-access<Const, Views...>; friend constexpr iterator operator-(const iterator& it, difference_type n) requires concat-is-random-access<Const, Views...>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires concat-is-random-access<Const, Views...>; friend constexpr difference_type operator-(const iterator& x, default_sentinel_t) requires see below; friend constexpr difference_type operator-(default_sentinel_t, const iterator& x) requires see below; friend constexpr decltype(auto) iter_move(const iterator& it) noexcept(see below); friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires see below; }; }
iterator​::​iterator_concept is defined as follows:
  • If concat-is-random-access<Const, Views...> is modeled, then iterator_concept denotes random_access_iterator_tag.
  • Otherwise, if concat-is-bidirectional<Const, Views...> is modeled, then iterator_concept denotes bidirectional_iterator_tag.
  • Otherwise, if all-forward<Const, Views...> is modeled, 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 all-forward<Const, Views...> is modeled.
In that case, iterator​::​iterator_category is defined as follows:
  • If is_reference_v<concat-reference-t<maybe-const<Const, Views>...>> is false, then iterator_category denotes input_iterator_tag.
  • Otherwise, let Cs denote the pack of types iterator_traits<iterator_t<maybe-const<Const, Views>>>​::​iterator_category....
    • If (derived_from<Cs, random_access_iterator_tag> && ...) && concat-is-random-ac-
      cess
      <Const, Views...>
      is true, iterator_category denotes random_access_iterator_tag.
    • Otherwise, if (derived_from<Cs, bidirectional_iterator_tag> && ...) && concat-is--
      bidirectional
      <Const, Views...>
      is true, iterator_category denotes bidirectional_iterator_tag.
    • Otherwise, if (derived_from<Cs, forward_iterator_tag> && ...) is true, iterator_category denotes forward_iterator_tag.
    • Otherwise, iterator_category denotes input_iterator_tag.
template<size_t N> constexpr void satisfy(); // exposition only
Effects: Equivalent to: if constexpr (N < (sizeof...(Views) - 1)) { if (std::get<N>(it_) == ranges::end(std::get<N>(parent_->views_))) { it_.template emplace<N + 1>(ranges::begin(std::get<N + 1>(parent_->views_))); satisfy<N + 1>(); } }
template<size_t N> constexpr void prev(); // exposition only
Effects: Equivalent to: if constexpr (N == 0) { --std::get<0>(it_); } else { if (std::get<N>(it_) == ranges::begin(std::get<N>(parent_->views_))) { it_.template emplace<N - 1>(ranges::end(std::get<N - 1>(parent_->views_))); prev<N - 1>(); } else { --std::get<N>(it_); } }
template<size_t N> constexpr void advance-fwd(difference_type offset, difference_type steps); // exposition only
Effects: Equivalent to: using underlying_diff_type = iter_difference_t<variant_alternative_t<N, base-iter>>; if constexpr (N == sizeof...(Views) - 1) { std::get<N>(it_) += static_cast<underlying_diff_type>(steps); } else { auto n_size = ranges::distance(std::get<N>(parent_->views_)); if (offset + steps < n_size) { std::get<N>(it_) += static_cast<underlying_diff_type>(steps); } else { it_.template emplace<N + 1>(ranges::begin(std::get<N + 1>(parent_->views_))); advance-fwd<N + 1>(0, offset + steps - n_size); } }
template<size_t N> constexpr void advance-bwd(difference_type offset, difference_type steps); // exposition only
Effects: Equivalent to: using underlying_diff_type = iter_difference_t<variant_alternative_t<N, base-iter>>; if constexpr (N == 0) { std::get<N>(it_) -= static_cast<underlying_diff_type>(steps); } else { if (offset >= steps) { std::get<N>(it_) -= static_cast<underlying_diff_type>(steps); } else { auto prev_size = ranges::distance(std::get<N - 1>(parent_->views_)); it_.template emplace<N - 1>(ranges::end(std::get<N - 1>(parent_->views_))); advance-bwd<N - 1>(prev_size, steps - offset); } }
template<class... Args> constexpr explicit iterator(maybe-const<Const, concat_view>* parent, // exposition only Args&&... args) requires constructible_from<base-iter, Args&&...>;
Effects: Initializes parent_ with parent, and initializes it_ with std​::​forward<Args>(args)....
constexpr iterator(iterator<!Const> it) requires Const && (convertible_to<iterator_t<Views>, iterator_t<const Views>> && ...);
Effects: Initializes parent_ with it.parent_, and let i be it.it_.index(), initializes it_ with base-iter(in_place_index<i>, std​::​get<i>(std​::​move(it.it_))).
constexpr decltype(auto) operator*() const;
Preconditions: it_.valueless_by_exception() is false.
Effects: Equivalent to: using reference = concat-reference-t<maybe-const<Const, Views>...>; return std::visit([](auto&& it) -> reference { return *it; }, it_);
constexpr iterator& operator++();
Preconditions: it_.valueless_by_exception() is false.
Effects: Let i be it_.index().
Equivalent to: ++std::get<i>(it_); satisfy<i>(); return *this;
constexpr void operator++(int);
Effects: Equivalent to: ++*this;
constexpr iterator operator++(int) requires all-forward<Const, Views...>;
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires concat-is-bidirectional<Const, Views...>;
Preconditions: it_.valueless_by_exception() is false.
Effects: Let i be it_.index().
Equivalent to: prev<i>(); return *this;
constexpr iterator operator--(int) requires concat-is-bidirectional<Const, Views...>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
constexpr iterator& operator+=(difference_type n) requires concat-is-random-access<Const, Views...>;
Preconditions: it_.valueless_by_exception() is false.
Effects: Let i be it_.index().
Equivalent to: if (n > 0) { advance-fwd<i>(std::get<i>(it_) - ranges::begin(std::get<i>(parent_->views_)), n); } else if (n < 0) { advance-bwd<i>(std::get<i>(it_) - ranges::begin(std::get<i>(parent_->views_)), -n); } return *this;
constexpr iterator& operator-=(difference_type n) requires concat-is-random-access<Const, Views...>;
Effects: Equivalent to: *this += -n; return *this;
constexpr decltype(auto) operator[](difference_type n) const requires concat-is-random-access<Const, Views...>;
Effects: Equivalent to: return *((*this) + n);
friend constexpr bool operator==(const iterator& x, const iterator& y) requires (equality_comparable<iterator_t<maybe-const<Const, Views>>> && ...);
Preconditions: x.it_.valueless_by_exception() and y.it_.valueless_by_exception() are each false.
Effects: Equivalent to: return x.it_ == y.it_;
friend constexpr bool operator==(const iterator& it, default_sentinel_t);
Preconditions: it.it_.valueless_by_exception() is false.
Effects: Equivalent to: constexpr auto last_idx = sizeof...(Views) - 1; return it.it_.index() == last_idx && std::get<last_idx>(it.it_) == ranges::end(std::get<last_idx>(it.parent_->views_));
friend constexpr bool operator<(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator>(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator<=(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr bool operator>=(const iterator& x, const iterator& y) requires all-random-access<Const, Views...>; friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires (all-random-access<Const, Views...> && (three_way_comparable<iterator_t<maybe-const<Const, Views>>> && ...));
Preconditions: x.it_.valueless_by_exception() and y.it_.valueless_by_exception() are each false.
Let op be the operator.
Effects: Equivalent to: return x.it_ op y.it_;
friend constexpr iterator operator+(const iterator& it, difference_type n) requires concat-is-random-access<Const, Views...>;
Effects: Equivalent to: auto temp = it; temp += n; return temp;
friend constexpr iterator operator+(difference_type n, const iterator& it) requires concat-is-random-access<Const, Views...>;
Effects: Equivalent to: return it + n;
friend constexpr iterator operator-(const iterator& it, difference_type n) requires concat-is-random-access<Const, Views...>;
Effects: Equivalent to: auto temp = it; temp -= n; return temp;
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires concat-is-random-access<Const, Views...>;
Preconditions: x.it_.valueless_by_exception() and y.it_.valueless_by_exception() are each false.
Effects: Let denote x.it_.index() and denote y.it_.index().
  • If > , let be ranges​::​distance(std​::​get<>(y.it_), ranges​::​end(std​::​get<>(y.
    parent_->views_)))
    , be ranges​::​distance(ranges​::​begin(std​::​get<>(x.parent_->
    views_)), std​::​get<>(x.it_))
    .
    Let s denote the sum of the sizes of all the ranges std​::​get<
    i>(x.parent_->views_)
    for every integer i in the range [ + 1, ) if there is any, and 0 otherwise, of type difference_type, equivalent to: return + s + ;
  • otherwise, if < is true, equivalent to: return -(y - x);
  • otherwise, equivalent to: return std::get<>(x.it_) - std::get<>(y.it_);
friend constexpr difference_type operator-(const iterator& x, default_sentinel_t) requires see below;
Preconditions: x.it_.valueless_by_exception() is false.
Effects: Let denote x.it_.index(), be ranges​::​distance(std​::​get<>(x.it_), ranges​::​
end(std​::​get<>(x.parent_->views_)))
.
Let s denote the sum of the sizes of all the ranges std​::​get<i>(x.parent_->views_) for every integer i in the range [ + 1, sizeof...(Views)) if there is any, and 0 otherwise, of type difference_type, equivalent to: return -( + s);
Remarks: Let Fs be the pack that consists of all elements of Views except the first element, the expression in the requires-clause is equivalent to: (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>>> && ...) && (sized_range<maybe-const<Const, Fs>> && ...)
friend constexpr difference_type operator-(default_sentinel_t, const iterator& x) requires see below;
Effects: Equivalent to: return -(x - default_sentinel);
Remarks: Let Fs be the pack that consists of all elements of Views except the first element, the expression in the requires-clause is equivalent to: (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>>> && ...) && (