[range.join] # 25 Ranges library [[ranges]](./#ranges) ## 25.7 Range adaptors [[range.adaptors]](range.adaptors#range.join) ### 25.7.14 Join view [range.join] #### [25.7.14.1](#overview) Overview [[range.join.overview]](range.join.overview) [1](#overview-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6650) join_view flattens a view of ranges into a view[.](#overview-1.sentence-1) [2](#overview-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6653) The name views​::​join denotes a range adaptor object ([[range.adaptor.object]](range.adaptor.object "25.7.2 Range adaptor objects"))[.](#overview-2.sentence-1) Given a subexpression E, the expressionviews​::​join(E) is expression-equivalent tojoin_view>{E}[.](#overview-2.sentence-2) [3](#overview-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6661) [*Example [1](#overview-example-1)*: vector ss{"hello", " ", "world", "!"};for (char ch : ss | views::join) cout << ch; // prints hello world! — *end example*] #### [25.7.14.2](#view) Class template join_view [[range.join.view]](range.join.view) [🔗](#lib:join_view) namespace std::ranges {template<[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]") V>requires [view](range.view#concept:view "25.4.5 Views [range.view]") && [input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")>class join_view : public view_interface> {private:using *InnerRng* = range_reference_t; // *exposition only*// [[range.join.iterator]](#iterator "25.7.14.3 Class template join_­view​::​iterator"), class template join_view​::​*iterator*templatestruct *iterator*; // *exposition only*// [[range.join.sentinel]](#sentinel "25.7.14.4 Class template join_­view​::​sentinel"), class template join_view​::​*sentinel*templatestruct *sentinel*; // *exposition only* V *base_* = V(); // *exposition only**non-propagating-cache*> *outer_*; // *exposition only*, present only// when ![forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")*non-propagating-cache*> *inner_*; // *exposition only*, present only// if is_reference_v<*InnerRng*> is falsepublic: join_view() requires [default_initializable](concept.default.init#concept:default_initializable "18.4.12 Concept default_­initializable [concept.default.init]") = default; constexpr explicit join_view(V base); constexpr V base() const & requires [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]") { return *base_*; }constexpr V base() && { return std::move(*base_*); }constexpr auto begin() {if constexpr ([forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")) {constexpr bool use_const = [*simple-view*](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]") && is_reference_v<*InnerRng*>; return *iterator*{*this, ranges::begin(*base_*)}; } else {*outer_* = ranges::begin(*base_*); return *iterator*{*this}; }}constexpr auto begin() constrequires [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") && is_reference_v> &&[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")>{ return *iterator*{*this, ranges::begin(*base_*)}; }constexpr auto end() {if constexpr ([forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") && is_reference_v<*InnerRng*> && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<*InnerRng*> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]") && [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<*InnerRng*>)return *iterator*<[*simple-view*](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")>{*this, ranges::end(*base_*)}; elsereturn *sentinel*<[*simple-view*](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")>{*this}; }constexpr auto end() constrequires [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") && is_reference_v> &&[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")> {if constexpr ([forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]") &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")>)return *iterator*{*this, ranges::end(*base_*)}; elsereturn *sentinel*{*this}; }}; templateexplicit join_view(R&&) -> join_view>;} [🔗](#lib:join_view,constructor) `constexpr explicit join_view(V base); ` [1](#view-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6756) *Effects*: Initializes *base_* with std​::​move(base)[.](#view-1.sentence-1) #### [25.7.14.3](#iterator) Class template join_view​::​*iterator* [[range.join.iterator]](range.join.iterator) [🔗](#lib:join_view::iterator) namespace std::ranges {template<[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]") V>requires [view](range.view#concept:view "25.4.5 Views [range.view]") && [input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")>templatestruct join_view::*iterator* {private:using *Parent* = *maybe-const*; // *exposition only*using *Base* = *maybe-const*; // *exposition only*using *OuterIter* = iterator_t<*Base*>; // *exposition only*using *InnerIter* = iterator_t>; // *exposition only*static constexpr bool *ref-is-glvalue* = // *exposition only* is_reference_v>; *OuterIter* *outer_* = *OuterIter*(); // *exposition only*, present only// if *Base* models [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") 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](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<*Base*>; // *exposition only*constexpr explicit *iterator*(*Parent*& parent)requires (![forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<*Base*>); // *exposition only*public:using iterator_concept = *see below*; using iterator_category = *see below*; // not always presentusing value_type = range_value_t>; using difference_type = *see below*; *iterator*() = default; constexpr *iterator*(*iterator* i)requires Const &&[convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), *OuterIter*> &&[convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), *InnerIter*>; constexpr decltype(auto) operator*() const { return ***inner_*; }constexpr *InnerIter* operator->() constrequires [*has-arrow*](range.utility.helpers#concept:has-arrow "25.5.2 Helper concepts [range.utility.helpers]")<*InnerIter*> && [copyable](concepts.object#concept:copyable "18.6 Object concepts [concepts.object]")<*InnerIter*>; constexpr *iterator*& operator++(); constexpr void operator++(int); constexpr *iterator* operator++(int)requires *ref-is-glvalue* && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<*Base*> &&[forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")>; constexpr *iterator*& operator--()requires *ref-is-glvalue* && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")<*Base*> &&[bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")>; constexpr *iterator* operator--(int)requires *ref-is-glvalue* && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")<*Base*> &&[bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")>; friend constexpr bool operator==(const *iterator*& x, const *iterator*& y)requires *ref-is-glvalue* && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<*Base*> &&[equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4 Concept equality_­comparable [concept.equalitycomparable]")>>; 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](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4 Concept indirectly_­swappable [alg.req.ind.swap]")<*InnerIter*>; };} [1](#iterator-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6843) *iterator*​::​iterator_concept is defined as follows: - [(1.1)](#iterator-1.1) If *ref-is-glvalue* is true, *Base* models [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]"), and range_reference_t<*Base*> models both [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]") and [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]"), then iterator_concept denotes bidirectional_iterator_tag[.](#iterator-1.1.sentence-1) - [(1.2)](#iterator-1.2) Otherwise, if *ref-is-glvalue* is true and *Base* and range_reference_t<*Base*> each model [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]"), then iterator_concept denotes forward_iterator_tag[.](#iterator-1.2.sentence-1) - [(1.3)](#iterator-1.3) Otherwise, iterator_concept denotes input_iterator_tag[.](#iterator-1.3.sentence-1) [2](#iterator-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6858) The member [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]") iterator_category is defined if and only if *ref-is-glvalue* is true,*Base* models [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]"), andrange_reference_t<*Base*> models [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")[.](#iterator-2.sentence-1) In that case,*iterator*​::​iterator_category is defined as follows: - [(2.1)](#iterator-2.1) Let *OUTERC* denote iterator_traits>​::​iterator_category, and let *INNERC* denote iterator_traits>>​::​iterator_category[.](#iterator-2.1.sentence-1) - [(2.2)](#iterator-2.2) If *OUTERC* and *INNERC* each model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]") and range_reference_t<*Base*> models [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]"), iterator_category denotes bidirectional_iterator_tag[.](#iterator-2.2.sentence-1) - [(2.3)](#iterator-2.3) Otherwise, if *OUTERC* and *INNERC* each model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]"), iterator_category denotes forward_iterator_tag[.](#iterator-2.3.sentence-1) - [(2.4)](#iterator-2.4) Otherwise, iterator_category denotes input_iterator_tag[.](#iterator-2.4.sentence-1) [3](#iterator-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6883) *iterator*​::​difference_type denotes the type:common_type_t< range_difference_t<*Base*>, range_difference_t>> [4](#iterator-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6891) join_view iterators use the *satisfy* function to skip over empty inner ranges[.](#iterator-4.sentence-1) [🔗](#lib:outer,join_view::iterator) `constexpr OuterIter& outer(); constexpr const OuterIter& outer() const; ` [5](#iterator-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6902) *Returns*: *outer_* if *Base* models [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]"); otherwise, **parent_*->*outer_*[.](#iterator-5.sentence-1) [🔗](#lib:satisfy,join_view::iterator) `constexpr void satisfy(); ` [6](#iterator-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6914) *Effects*: Equivalent to:auto update_inner = [this](const iterator_t<*Base*>& x) -> auto&& {if constexpr (*ref-is-glvalue*) // *x is a referencereturn *x; elsereturn *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(); [🔗](#lib:join_view::iterator,constructor) `constexpr iterator(Parent& parent, OuterIter outer) requires [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]"); ` [7](#iterator-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6943) *Effects*: Initializes *outer_* with std​::​move(outer) and*parent_* with addressof(parent); then calls *satisfy*()[.](#iterator-7.sentence-1) [🔗](#lib:join_view::iterator,constructor_) `constexpr explicit iterator(Parent& parent) requires (![forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")); ` [8](#iterator-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6956) *Effects*: Initializes *parent_* with addressof(parent); then calls *satisfy*()[.](#iterator-8.sentence-1) [🔗](#lib:join_view::iterator,constructor__) `constexpr iterator(iterator i) requires Const && [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), OuterIter> && [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), InnerIter>; ` [9](#iterator-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6971) *Effects*: Initializes *outer_* with std​::​move(i.*outer_*),*inner_* with std​::​move(i.*inner_*), and*parent_* with i.*parent_*[.](#iterator-9.sentence-1) [10](#iterator-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6977) [*Note [1](#iterator-note-1)*: Const can only be true when *Base* models [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")[.](#iterator-10.sentence-1) — *end note*] [🔗](#lib:operator-%3e,join_view::iterator) `constexpr InnerIter operator->() const requires [has-arrow](range.utility.helpers#concept:has-arrow "25.5.2 Helper concepts [range.utility.helpers]") && [copyable](concepts.object#concept:copyable "18.6 Object concepts [concepts.object]"); ` [11](#iterator-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6991) *Effects*: Equivalent to: return **inner_*; [🔗](#lib:operator++,join_view::iterator) `constexpr iterator& operator++(); ` [12](#iterator-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7002) Let *inner-range* be: - [(12.1)](#iterator-12.1) If *ref-is-glvalue* is true, **outer*()[.](#iterator-12.1.sentence-1) - [(12.2)](#iterator-12.2) Otherwise, **parent_*->*inner_*[.](#iterator-12.2.sentence-1) [13](#iterator-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7009) *Effects*: Equivalent to:if (++**inner_* == ranges::end(*as-lvalue*(*inner-range*))) {++*outer*(); *satisfy*();}return *this; [🔗](#lib:operator++,join_view::iterator_) `constexpr void operator++(int); ` [14](#iterator-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7027) *Effects*: Equivalent to: ++*this[.](#iterator-14.sentence-1) [🔗](#lib:operator++,join_view::iterator__) `constexpr iterator operator++(int) requires ref-is-glvalue && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")>; ` [15](#iterator-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7040) *Effects*: Equivalent to:auto tmp = *this;++*this;return tmp; [🔗](#lib:operator--,join_view::iterator) `constexpr iterator& operator--() requires ref-is-glvalue && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]") && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")> && [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")>; ` [16](#iterator-16) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7059) *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; [🔗](#lib:operator--,join_view::iterator_) `constexpr iterator operator--(int) requires ref-is-glvalue && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]") && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")> && [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")>; ` [17](#iterator-17) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7081) *Effects*: Equivalent to:auto tmp = *this;--*this;return tmp; [🔗](#lib:operator==,join_view::iterator) `friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]") && [equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4 Concept equality_­comparable [concept.equalitycomparable]")>>; ` [18](#iterator-18) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7099) *Effects*: Equivalent to:return x.*outer_* == y.*outer_* && x.*inner_* == y.*inner_*; [🔗](#lib:iter_swap,join_view::iterator) `friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires [indirectly_swappable](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4 Concept indirectly_­swappable [alg.req.ind.swap]"); ` [19](#iterator-19) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7113) *Effects*: Equivalent to: ranges​::​iter_swap(*x.*inner_*, *y.*inner_*); #### [25.7.14.4](#sentinel) Class template join_view​::​*sentinel* [[range.join.sentinel]](range.join.sentinel) [🔗](#lib:join_view::sentinel) namespace std::ranges {template<[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]") V>requires [view](range.view#concept:view "25.4.5 Views [range.view]") && [input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")>templatestruct join_view::*sentinel* {private:using *Parent* = *maybe-const*; // *exposition only*using *Base* = *maybe-const*; // *exposition only* sentinel_t<*Base*> *end_* = sentinel_t<*Base*>(); // *exposition only*public:*sentinel*() = default; constexpr explicit *sentinel*(*Parent*& parent); constexpr *sentinel*(*sentinel* s)requires Const && [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), sentinel_t<*Base*>>; templaterequires [sentinel_for](iterator.concept.sentinel#concept:sentinel_for "24.3.4.7 Concept sentinel_­for [iterator.concept.sentinel]"), iterator_t<*maybe-const*>>friend constexpr bool operator==(const *iterator*& x, const *sentinel*& y); };} [🔗](#lib:join_view::sentinel,constructor) `constexpr explicit sentinel(Parent& parent); ` [1](#sentinel-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7152) *Effects*: Initializes *end_* with ranges​::​end(parent.*base_*)[.](#sentinel-1.sentence-1) [🔗](#lib:join_view::sentinel,constructor_) `constexpr sentinel(sentinel s) requires Const && [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]"), sentinel_t>; ` [2](#sentinel-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7164) *Effects*: Initializes *end_* with std​::​move(s.*end_*)[.](#sentinel-2.sentence-1) [🔗](#lib:operator==,join_view::sentinel) `template requires [sentinel_for](iterator.concept.sentinel#concept:sentinel_for "24.3.4.7 Concept sentinel_­for [iterator.concept.sentinel]"), iterator_t>> friend constexpr bool operator==(const iterator& x, const sentinel& y); ` [3](#sentinel-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7177) *Effects*: Equivalent to: return x.*outer*() == y.*end_*;