[range.concat]

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

## 25.7 Range adaptors [[range.adaptors]](range.adaptors#range.concat)

### 25.7.18 Concat view [range.concat]

#### [25.7.18.1](#overview) Overview [[range.concat.overview]](range.concat.overview)

[1](#overview-1)

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

concat_view presents a view that concatenates all the underlying ranges[.](#overview-1.sentence-1)

[2](#overview-2)

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

The name views​::​concat denotes
a customization point object ([[customization.point.object]](customization.point.object "16.3.3.3.5 Customization Point Object types"))[.](#overview-2.sentence-1)

Given a pack of subexpressions Es...,
the expression views​::​concat(Es...) is expression-equivalent to

- [(2.1)](#overview-2.1)

views​::​all(Es...) if Es is a pack with only one element
whose type models [input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]"),

- [(2.2)](#overview-2.2)

otherwise, concat_view(Es...)[.](#overview-2.sentence-2)

[*Example [1](#overview-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*]

#### [25.7.18.2](#view) Class template concat_view [[range.concat.view]](range.concat.view)

[ð](#lib: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*](#concept:concat-indirectly-readable "25.7.18.2 Class template concat_­view [range.concat.view]") = *see below*; // *exposition only*template<class... Rs>concept [*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]") = *see below*; // *exposition only*template<bool Const, class... Rs>concept [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]") = *see below*; // *exposition only*template<bool Const, class... Rs>concept [*concat-is-bidirectional*](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]") = *see below*; // *exposition only*template<[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")... Views>requires ([view](range.view#concept:view "25.4.5 Views [range.view]")<Views> && ...) && (sizeof...(Views) > 0) &&[*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<Views...>class concat_view : public view_interface<concat_view<Views...>> { tuple<Views...> *views_*; // *exposition only*// [[range.concat.iterator]](#iterator "25.7.18.3 Class concat_­view​::​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*](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")<Views> && ...)); constexpr *iterator*<true> begin() constrequires ([range](range.range#concept:range "25.4.2 Ranges [range.range]")<const Views> && ...) && [*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<const Views...>; constexpr auto end() requires (!([*simple-view*](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")<Views> && ...)); constexpr auto end() constrequires ([range](range.range#concept:range "25.4.2 Ranges [range.range]")<const Views> && ...) && [*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<const Views...>; constexpr auto size() requires ([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<Views> && ...); constexpr auto size() const requires ([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<const Views> && ...); }; template<class... R> concat_view(R&&...) -> concat_view<views::all_t<R>...>;}

[ð](#concept:concat-indirectly-readable)

`template<class... Rs>
  concept [concat-indirectly-readable](#concept:concat-indirectly-readable "25.7.18.2 Class template concat_­view [range.concat.view]") = see below;                           // exposition only
`

[1](#view-1)

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

The exposition-only [*concat-indirectly-readable*](#concept:concat-indirectly-readable "25.7.18.2 Class template concat_­view [range.concat.view]") concept
is equivalent to:template<class Ref, class RRef, class It>concept [*concat-indirectly-readable-impl*](#concept:concat-indirectly-readable-impl "25.7.18.2 Class template concat_­view [range.concat.view]") = // *exposition only*requires (const It it) {{ *it } -> [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<Ref>; { ranges::iter_move(it) } -> [convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<RRef>; };

template<class... Rs>concept [*concat-indirectly-readable*](#concept:concat-indirectly-readable "25.7.18.2 Class template concat_­view [range.concat.view]") = // *exposition only*[common_reference_with](concept.commonref#concept:common_reference_with "18.4.5 Concept common_­reference_­with [concept.commonref]")<*concat-reference-t*<Rs...>&&, *concat-value-t*<Rs...>&> &&[common_reference_with](concept.commonref#concept:common_reference_with "18.4.5 Concept common_­reference_­with [concept.commonref]")<*concat-reference-t*<Rs...>&&, *concat-rvalue-reference-t*<Rs...>&&> &&[common_reference_with](concept.commonref#concept:common_reference_with "18.4.5 Concept common_­reference_­with [concept.commonref]")<*concat-rvalue-reference-t*<Rs...>&&, *concat-value-t*<Rs...> const&> &&([*concat-indirectly-readable-impl*](#concept:concat-indirectly-readable-impl "25.7.18.2 Class template concat_­view [range.concat.view]")<*concat-reference-t*<Rs...>, *concat-rvalue-reference-t*<Rs...>,
 iterator_t<Rs>> && ...);

[ð](#concept:concatable)

`template<class... Rs>
  concept [concatable](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]") = see below;                                           // exposition only
`

[2](#view-2)

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

The exposition-only [*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]") concept is equivalent to:template<class... Rs>concept [*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]") = requires { // *exposition only*typename *concat-reference-t*<Rs...>; typename *concat-value-t*<Rs...>; typename *concat-rvalue-reference-t*<Rs...>; } && [*concat-indirectly-readable*](#concept:concat-indirectly-readable "25.7.18.2 Class template concat_­view [range.concat.view]")<Rs...>;

[ð](#concept:concat-is-random-access)

`template<bool Const, class... Rs>
  concept [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]") = see below;                              // exposition only
`

[3](#view-3)

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

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*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]") = // *exposition only*[*all-random-access*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Rs...> &&([common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<*maybe-const*<Const, Fs>> && ...);

[ð](#concept:concat-is-bidirectional)

`template<bool Const, class... Rs>
  concept [concat-is-bidirectional](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]") = see below;                              // exposition only
`

[4](#view-4)

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

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*](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]") = // *exposition only*[*all-bidirectional*](range.adaptor.helpers#concept:all-bidirectional "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Rs...> &&([common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<*maybe-const*<Const, Fs>> && ...);

[ð](#lib:concat_view,constructor)

`constexpr explicit concat_view(Views... views);
`

[5](#view-5)

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

*Effects*: Initializes *views_* with std​::​move(views)...[.](#view-5.sentence-1)

[ð](#lib:begin,concat_view)

`constexpr iterator<false> begin() requires (!([simple-view](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")<Views> && ...));
constexpr iterator<true> begin() const
  requires ([range](range.range#concept:range "25.4.2 Ranges [range.range]")<const Views> && ...) && [concatable](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<const Views...>;
`

[6](#view-6)

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

*Effects*: Let *is-const* betrue for the const-qualified overload, andfalse otherwise[.](#view-6.sentence-1)

Equivalent to:*iterator*<*is-const*> it(this, in_place_index<0>, ranges::begin(std::get<0>(*views_*)));
it.template *satisfy*<0>();return it;

[ð](#lib:end,concat_view)

`constexpr auto end() requires (!([simple-view](range.utility.helpers#concept:simple-view "25.5.2 Helper concepts [range.utility.helpers]")<Views> && ...));
constexpr auto end() const
  requires ([range](range.range#concept:range "25.4.2 Ranges [range.range]")<const Views> && ...) && [concatable](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<const Views...>;
`

[7](#view-7)

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

*Effects*: Let *is-const* betrue for the const-qualified overload, andfalse otherwise[.](#view-7.sentence-1)

Equivalent to:constexpr auto N = sizeof...(Views);if constexpr ([common_range](range.refinements#concept:common_range "25.4.6 Other range refinements [range.refinements]")<*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;}

[ð](#lib:size,concat_view)

`constexpr auto size() requires ([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<Views> && ...);
constexpr auto size() const requires ([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<const Views> && ...);
`

[8](#view-8)

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

*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_*));

#### [25.7.18.3](#iterator) Class concat_view​::​*iterator* [[range.concat.iterator]](range.concat.iterator)

[ð](#lib:iterator,concat_view)

namespace std::ranges {template<[input_range](range.refinements#concept:input_range "25.4.6 Other range refinements [range.refinements]")... Views>requires ([view](range.view#concept:view "25.4.5 Views [range.view]")<Views> && ...) && (sizeof...(Views) > 0) &&[*concatable*](#concept:concatable "25.7.18.2 Class template concat_­view [range.concat.view]")<Views...>template<bool Const>class concat_view<Views...>::*iterator* {public:using iterator_category = *see below*; // not always presentusing 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](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]")<*base-iter*, Args&&...>; public:*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<Views>, iterator_t<const Views>> && ...); constexpr decltype(auto) operator*() const; constexpr *iterator*& operator++(); constexpr void operator++(int); constexpr *iterator* operator++(int)requires [*all-forward*](range.adaptor.helpers#concept:all-forward "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>; constexpr *iterator*& operator--()requires [*concat-is-bidirectional*](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; constexpr *iterator* operator--(int)requires [*concat-is-bidirectional*](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; constexpr *iterator*& operator+=(difference_type n)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; constexpr *iterator*& operator-=(difference_type n)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; constexpr decltype(auto) operator[](difference_type n) constrequires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; friend constexpr bool operator==(const *iterator*& x, const *iterator*& y)requires ([equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4 Concept equality_­comparable [concept.equalitycomparable]")<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*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>; friend constexpr bool operator>(const *iterator*& x, const *iterator*& y)requires [*all-random-access*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>; friend constexpr bool operator<=(const *iterator*& x, const *iterator*& y)requires [*all-random-access*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>; friend constexpr bool operator>=(const *iterator*& x, const *iterator*& y)requires [*all-random-access*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>; friend constexpr auto operator<=>(const *iterator*& x, const *iterator*& y)requires ([*all-random-access*](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...> &&([three_way_comparable](cmp.concept#concept:three_way_comparable "17.12.4 Concept three_­way_­comparable [cmp.concept]")<iterator_t<*maybe-const*<Const, Views>>> && ...)); friend constexpr *iterator* operator+(const *iterator*& it, difference_type n)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; friend constexpr *iterator* operator+(difference_type n, const *iterator*& it)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; friend constexpr *iterator* operator-(const *iterator*& it, difference_type n)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>; friend constexpr difference_type operator-(const *iterator*& x, const *iterator*& y)requires [*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<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*; };}

[1](#iterator-1)

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

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

- [(1.1)](#iterator-1.1)

  If[*concat-is-random-access*](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...> is modeled,
then iterator_concept denotes random_access_iterator_tag[.](#iterator-1.1.sentence-1)

- [(1.2)](#iterator-1.2)

  Otherwise, if[*concat-is-bidirectional*](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...> is modeled,
then iterator_concept denotes bidirectional_iterator_tag[.](#iterator-1.2.sentence-1)

- [(1.3)](#iterator-1.3)

  Otherwise, if[*all-forward*](range.adaptor.helpers#concept:all-forward "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...> is modeled,
then iterator_concept denotes forward_iterator_tag[.](#iterator-1.3.sentence-1)

- [(1.4)](#iterator-1.4)

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

[2](#iterator-2)

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

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[*all-forward*](range.adaptor.helpers#concept:all-forward "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...> is modeled[.](#iterator-2.sentence-1)

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

- [(2.1)](#iterator-2.1)

  Ifis_reference_v<*concat-reference-t*<*maybe-const*<Const, Views>...>> is false,
then iterator_category denotes input_iterator_tag[.](#iterator-2.1.sentence-1)

- [(2.2)](#iterator-2.2)

  Otherwise,
let Cs denote the pack of typesiterator_traits<iterator_t<*maybe-const*<Const, Views>>>​::​iterator_category...[.](#iterator-2.2.sentence-1)

  * [(2.2.1)](#iterator-2.2.1)

      If([derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")<Cs, random_access_iterator_tag> && ...) && *concat-is-random-access*<Const, Views...> is true,iterator_category denotes random_access_iterator_tag[.](#iterator-2.2.1.sentence-1)

  * [(2.2.2)](#iterator-2.2.2)

      Otherwise, if([derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")<Cs, bidirectional_iterator_tag> && ...) && *concat-is-bidirectional*<Const, Views...> is true,iterator_category denotes bidirectional_iterator_tag[.](#iterator-2.2.2.sentence-1)

  * [(2.2.3)](#iterator-2.2.3)

      Otherwise, if([derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_­from [concept.derived]")<Cs, forward_iterator_tag> && ...) is true,iterator_category denotes forward_iterator_tag[.](#iterator-2.2.3.sentence-1)

  * [(2.2.4)](#iterator-2.2.4)

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

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

`template<size_t N>
  constexpr void satisfy();
`

[3](#iterator-3)

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

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

[ð](#lib:prev,concat_view::iterator)

`template<size_t N>
  constexpr void prev();
`

[4](#iterator-4)

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

*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_*); }}

[ð](#lib:advance-fwd,concat_view::iterator)

`template<size_t N>
  constexpr void advance-fwd(difference_type offset, difference_type steps);
`

[5](#iterator-5)

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

*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); }}

[ð](#lib:advance-bwd,concat_view::iterator)

`template<size_t N>
  constexpr void advance-bwd(difference_type offset, difference_type steps);
`

[6](#iterator-6)

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

*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); }}

[ð](#lib:concat_view::iterator,constructor)

`template<class... Args>
  constexpr explicit iterator(maybe-const<Const, concat_view>* parent,
                              Args&&... args)
    requires [constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]")<base-iter, Args&&...>;
`

[7](#iterator-7)

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

*Effects*: Initializes *parent_* with parent, and
initializes *it_* with std​::​forward<Args>(args)...[.](#iterator-7.sentence-1)

[ð](#lib:concat_view::iterator,constructor_)

`constexpr iterator(iterator<!Const> it)
  requires Const &&
           ([convertible_to](concept.convertible#concept:convertible_to "18.4.4 Concept convertible_­to [concept.convertible]")<iterator_t<Views>, iterator_t<const Views>> && ...);
`

[8](#iterator-8)

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

*Preconditions*: it.*it_*.valueless_by_exception() is false[.](#iterator-8.sentence-1)

[9](#iterator-9)

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

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

[ð](#lib:operator*,concat_view::iterator)

`constexpr decltype(auto) operator*() const;
`

[10](#iterator-10)

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

*Preconditions*: *it_*.valueless_by_exception() is false[.](#iterator-10.sentence-1)

[11](#iterator-11)

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

*Effects*: Equivalent to:using reference = *concat-reference-t*<*maybe-const*<Const, Views>...>;return std::visit([](auto&& it) -> reference { return *it; }, *it_*);

[ð](#lib:operator++,concat_view::iterator)

`constexpr iterator& operator++();
`

[12](#iterator-12)

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

*Preconditions*: *it_*.valueless_by_exception() is false[.](#iterator-12.sentence-1)

[13](#iterator-13)

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

*Effects*: Let i be *it_*.index()[.](#iterator-13.sentence-1)

Equivalent to:++std::get<i>(*it_*);*satisfy*<i>();return *this;

[ð](#lib:operator++,concat_view::iterator_)

`constexpr void operator++(int);
`

[14](#iterator-14)

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

*Effects*: Equivalent to:++*this;

[ð](#lib:operator++,concat_view::iterator__)

`constexpr iterator operator++(int)
  requires [all-forward](range.adaptor.helpers#concept:all-forward "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>;
`

[15](#iterator-15)

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

*Effects*: Equivalent to:auto tmp = *this;++*this;return tmp;

[ð](#lib:operator--,concat_view::iterator)

`constexpr iterator& operator--()
  requires [concat-is-bidirectional](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[16](#iterator-16)

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

*Preconditions*: *it_*.valueless_by_exception() is false[.](#iterator-16.sentence-1)

[17](#iterator-17)

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

*Effects*: Let i be *it_*.index()[.](#iterator-17.sentence-1)

Equivalent to:*prev*<i>();return *this;

[ð](#lib:operator--,concat_view::iterator_)

`constexpr iterator operator--(int)
  requires [concat-is-bidirectional](#concept:concat-is-bidirectional "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[18](#iterator-18)

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

*Effects*: Equivalent to:auto tmp = *this;--*this;return tmp;

[ð](#lib:operator+=,concat_view::iterator)

`constexpr iterator& operator+=(difference_type n)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[19](#iterator-19)

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

*Preconditions*: *it_*.valueless_by_exception() is false[.](#iterator-19.sentence-1)

[20](#iterator-20)

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

*Effects*: Let i be *it_*.index()[.](#iterator-20.sentence-1)

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;

[ð](#lib:operator-=,concat_view::iterator)

`constexpr iterator& operator-=(difference_type n)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[21](#iterator-21)

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

*Effects*: Equivalent to:*this += -n;return *this;

[ð](#lib:operator%5b%5d,concat_view::iterator)

`constexpr decltype(auto) operator[](difference_type n) const
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[22](#iterator-22)

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

*Effects*: Equivalent to:return *((*this) + n);

[ð](#lib:operator==,concat_view::iterator)

`friend constexpr bool operator==(const iterator& x, const iterator& y)
  requires ([equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4 Concept equality_­comparable [concept.equalitycomparable]")<iterator_t<maybe-const<Const, Views>>> && ...);
`

[23](#iterator-23)

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

*Preconditions*: x.*it_*.valueless_by_exception() andy.*it_*.valueless_by_exception() are each false[.](#iterator-23.sentence-1)

[24](#iterator-24)

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

*Effects*: Equivalent to:return x.*it_* == y.*it_*;

[ð](#lib:operator==,concat_view::iterator_)

`friend constexpr bool operator==(const iterator& it, default_sentinel_t);
`

[25](#iterator-25)

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

*Preconditions*: it.*it_*.valueless_by_exception() is false[.](#iterator-25.sentence-1)

[26](#iterator-26)

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

*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_*));

[ð](#lib:operator%3c,concat_view::iterator)

`friend constexpr bool operator<(const iterator& x, const iterator& y)
  requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>;
friend constexpr bool operator>(const iterator& x, const iterator& y)
  requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>;
friend constexpr bool operator<=(const iterator& x, const iterator& y)
  requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>;
friend constexpr bool operator>=(const iterator& x, const iterator& y)
  requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...>;
friend constexpr auto operator<=>(const iterator& x, const iterator& y)
  requires ([all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5 Range adaptor helpers [range.adaptor.helpers]")<Const, Views...> &&
            ([three_way_comparable](cmp.concept#concept:three_way_comparable "17.12.4 Concept three_­way_­comparable [cmp.concept]")<iterator_t<maybe-const<Const, Views>>> && ...));
`

[27](#iterator-27)

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

*Preconditions*: x.*it_*.valueless_by_exception() andy.*it_*.valueless_by_exception() are each false[.](#iterator-27.sentence-1)

[28](#iterator-28)

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

Let op be the operator[.](#iterator-28.sentence-1)

[29](#iterator-29)

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

*Effects*: Equivalent to:return x.*it_* op y.*it_*;

[ð](#lib:operator+,concat_view::iterator)

`friend constexpr iterator operator+(const iterator& it, difference_type n)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[30](#iterator-30)

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

*Effects*: Equivalent to:auto temp = it;
temp += n;return temp;

[ð](#lib:operator+,concat_view::iterator_)

`friend constexpr iterator operator+(difference_type n, const iterator& it)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[31](#iterator-31)

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

*Effects*: Equivalent to:return it + n;

[ð](#lib:operator-,concat_view::iterator)

`friend constexpr iterator operator-(const iterator& it, difference_type n)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[32](#iterator-32)

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

*Effects*: Equivalent to:auto temp = it;
temp -= n;return temp;

[ð](#lib:operator-,concat_view::iterator_)

`friend constexpr difference_type operator-(const iterator& x, const iterator& y)
  requires [concat-is-random-access](#concept:concat-is-random-access "25.7.18.2 Class template concat_­view [range.concat.view]")<Const, Views...>;
`

[33](#iterator-33)

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

*Preconditions*: x.*it_*.valueless_by_exception() andy.*it_*.valueless_by_exception() are each false[.](#iterator-33.sentence-1)

[34](#iterator-34)

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

*Effects*: Letix denote x.*it_*.index() andiy denote y.*it_*.index()[.](#iterator-34.sentence-1)

- [(34.1)](#iterator-34.1)

If ix > iy, letdy beranges​::​distance(std​::​get<iy>(y.*it_*), ranges​::​end(std​::​get<iy>(y.*parent_*->*views_*))),dx beranges​::​distance(ranges​::​begin(std​::​get<ix>(x.*parent_*->*views_*)), std​::​get<ix>(x.*it_*)). Let s denote the sum of the sizes of all the rangesstd​::​get<i>(x.*parent_*->*views_*) for every integer i in the range
[iy + 1, ix)
if there is any, and0 otherwise,
of type difference_type,
equivalent to:return dy + s + dx;

- [(34.2)](#iterator-34.2)

otherwise, if ix < iy is true,
equivalent to:return -(y - x);

- [(34.3)](#iterator-34.3)

otherwise, equivalent to:return std::get<ix>(x.*it_*) - std::get<iy>(y.*it_*);

[ð](#lib:operator-,concat_view::iterator__)

`friend constexpr difference_type operator-(const iterator& x, default_sentinel_t)
  requires see below;
`

[35](#iterator-35)

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

*Preconditions*: x.*it_*.valueless_by_exception() is false[.](#iterator-35.sentence-1)

[36](#iterator-36)

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

*Effects*: Letix denote x.*it_*.index(),dx beranges​::​distance(std​::​get<ix>(x.*it_*), ranges​::​end(std​::​get<ix>(x.*parent_*->*views_*)))[.](#iterator-36.sentence-1)

Let s denote the sum of the sizes of all the rangesstd​::​get<i>(x.*parent_*->*views_*) for every integer i in the range
[ix + 1, sizeof...(Views))
if there is any, and0 otherwise,
of type difference_type,
equivalent to:return -(dx + s);

[37](#iterator-37)

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

*Remarks*: Let Fs be the pack that consists of all elements of Views except the first element,
the expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") is equivalent to:([sized_sentinel_for](iterator.concept.sizedsentinel#concept:sized_sentinel_for "24.3.4.8 Concept sized_­sentinel_­for [iterator.concept.sizedsentinel]")<sentinel_t<*maybe-const*<Const, Views>>,
 iterator_t<*maybe-const*<Const, Views>>> && ...) &&([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<*maybe-const*<Const, Fs>> && ...)

[ð](#lib:operator-,concat_view::iterator___)

`friend constexpr difference_type operator-(default_sentinel_t, const iterator& x)
  requires see below;
`

[38](#iterator-38)

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

*Effects*: Equivalent to:return -(x - default_sentinel);

[39](#iterator-39)

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

*Remarks*: Let Fs be the pack that consists of all elements of Views except the first element,
the expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") is equivalent to:([sized_sentinel_for](iterator.concept.sizedsentinel#concept:sized_sentinel_for "24.3.4.8 Concept sized_­sentinel_­for [iterator.concept.sizedsentinel]")<sentinel_t<*maybe-const*<Const, Views>>,
 iterator_t<*maybe-const*<Const, Views>>> && ...) &&([sized_range](range.sized#concept:sized_range "25.4.4 Sized ranges [range.sized]")<*maybe-const*<Const, Fs>> && ...)

[ð](#lib:iter_move,concat_view::iterator)

`friend constexpr decltype(auto) iter_move(const iterator& it) noexcept(see below);
`

[40](#iterator-40)

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

*Preconditions*: it.*it_*.valueless_by_exception() is false[.](#iterator-40.sentence-1)

[41](#iterator-41)

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

*Effects*: Equivalent to:return std::visit([](const auto& i)-> *concat-rvalue-reference-t*<*maybe-const*<Const, Views>...> {return ranges::iter_move(i); },
 it.*it_*);

[42](#iterator-42)

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

*Remarks*: The exception specification is equivalent to:((is_nothrow_invocable_v<decltype(ranges::iter_move), const iterator_t<*maybe-const*<Const, Views>>&> && is_nothrow_convertible_v<range_rvalue_reference_t<*maybe-const*<Const, Views>>, *concat-rvalue-reference-t*<*maybe-const*<Const, Views>...>>) &&...)

[ð](#lib:iter_swap,concat_view::iterator)

`friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
  requires see below;
`

[43](#iterator-43)

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

*Preconditions*: x.*it_*.valueless_by_exception() andy.*it_*.valueless_by_exception() are each false[.](#iterator-43.sentence-1)

[44](#iterator-44)

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

*Effects*: Equivalent to:std::visit([&](const auto& it1, const auto& it2) {if constexpr (is_same_v<decltype(it1), decltype(it2)>) { ranges::iter_swap(it1, it2); } else { ranges::swap(*x, *y); }},
 x.*it_*, y.*it_*);

[45](#iterator-45)

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

*Remarks*: The exception specification is equivalent to(noexcept(ranges::swap(*x, *y)) && ... && noexcept(ranges::iter_swap(its, its))) where its is a pack of lvalues of typeconst iterator_t<*maybe-const*<Const, Views>> respectively[.](#iterator-45.sentence-1)

The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") is equivalent to[swappable_with](concept.swappable#concept:swappable_with "18.4.9 Concept swappable [concept.swappable]")<iter_reference_t<*iterator*>, iter_reference_t<*iterator*>> &&(... && [indirectly_swappable](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4 Concept indirectly_­swappable [alg.req.ind.swap]")<iterator_t<*maybe-const*<Const, Views>>>)
