[range.join.iterator]

# 25 Ranges library [[ranges]](./#ranges)

## 25.7 Range adaptors [[range.adaptors]](range.adaptors#range.join.iterator)

### 25.7.14 Join view [[range.join]](range.join#iterator)

#### 25.7.14.3 Class template join_view​::​*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]")<V> && [input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")<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](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<range_reference_t<*Base*>>; using difference_type = *see below*; *iterator*() = default; constexpr *iterator*(*iterator*<!Const> i)requires Const &&[convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<iterator_t<V>, *OuterIter*> &&[convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<iterator_t<*InnerRng*>, *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]")<range_reference_t<*Base*>>; 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]")<range_reference_t<*Base*>> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<*Base*>>; 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]")<range_reference_t<*Base*>> &&[common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<*Base*>>; 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]")<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](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4 Concept indirectly_­swappable [alg.req.ind.swap]")<*InnerIter*>; };}

[1](#1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6843)

*iterator*​::​iterator_concept is defined as follows:

- [(1.1)](#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[.](#1.1.sentence-1)

- [(1.2)](#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[.](#1.2.sentence-1)

- [(1.3)](#1.3)

  Otherwise, iterator_concept denotes input_iterator_tag[.](#1.3.sentence-1)

[2](#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]")[.](#2.sentence-1)

In that case,*iterator*​::​iterator_category is defined as follows:

- [(2.1)](#2.1)

  Let *OUTERC* denote iterator_traits<iterator_t<*Base*>>​::​iterator_category, and
 let *INNERC* denote iterator_traits<iterator_t<range_reference_t<*Base*>>>​::​iterator_category[.](#2.1.sentence-1)

- [(2.2)](#2.2)

  If *OUTERC* and *INNERC* each model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")<bidirectional_iterator_tag> 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[.](#2.2.sentence-1)

- [(2.3)](#2.3)

  Otherwise, if *OUTERC* and *INNERC* each model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")<forward_iterator_tag>, iterator_category denotes forward_iterator_tag[.](#2.3.sentence-1)

- [(2.4)](#2.4)

  Otherwise, iterator_category denotes input_iterator_tag[.](#2.4.sentence-1)

[3](#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<range_reference_t<*Base*>>>

[4](#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[.](#4.sentence-1)

[ð](#lib:outer,join_view::iterator)

`constexpr OuterIter& outer();
constexpr const OuterIter& outer() const;
`

[5](#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_*[.](#5.sentence-1)

[ð](#lib:satisfy,join_view::iterator)

`constexpr void satisfy();
`

[6](#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]")<Base>;
`

[7](#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*()[.](#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]")<Base>);
`

[8](#8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6956)

*Effects*: Initializes *parent_* with addressof(parent);
then calls *satisfy*()[.](#8.sentence-1)

[ð](#lib:join_view::iterator,constructor__)

`constexpr iterator(iterator<!Const> i)
  requires Const &&
           [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<iterator_t<V>, OuterIter> &&
           [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<iterator_t<InnerRng>, InnerIter>;
`

[9](#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_*[.](#9.sentence-1)

[10](#10)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L6977)

[*Note [1](#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]")[.](#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]")<InnerIter> && [copyable](concepts.object#concept:copyable "18.6 Object concepts [concepts.object]")<InnerIter>;
`

[11](#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](#12)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7002)

Let *inner-range* be:

- [(12.1)](#12.1)

  If *ref-is-glvalue* is true, **outer*()[.](#12.1.sentence-1)

- [(12.2)](#12.2)

  Otherwise, **parent_*->*inner_*[.](#12.2.sentence-1)

[13](#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](#14)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7027)

*Effects*: Equivalent to: ++*this[.](#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]")<Base> &&
           [forward_range](range.refinements#concept:forward_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<Base>>;
`

[15](#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]")<Base> &&
           [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<Base>> &&
           [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<Base>>;
`

[16](#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]")<Base> &&
           [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<Base>> &&
           [common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<range_reference_t<Base>>;
`

[17](#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]")<Base> &&
           [equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4 Concept equality_­comparable [concept.equalitycomparable]")<iterator_t<range_reference_t<Base>>>;
`

[18](#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]")<InnerIter>;
`

[19](#19)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/ranges.tex#L7113)

*Effects*: Equivalent to: ranges​::​iter_swap(*x.*inner_*, *y.*inner_*);
