#
concat_view presents a view that concatenates all the underlying ranges.
2
#
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 whose type models input_range,
  • 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]

25.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 (all-forward<is-const, Views..> && 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_));

25.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-access<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();
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();
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);
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);
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, 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;
Effects: Equivalent to: using reference = concat-reference-t<maybe-const<Const, Views>.>; return std::visit([](auto& it) -> reference { return *it; }, it_);
constexpr iterator& operator+();
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..>;
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..>;
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);
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.
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
  • If
  • otherwise, if
  • otherwise, equivalent to: return std::get<
friend constexpr difference_type operator-(const iterator& x, default_sentinel_t) requires see below;
Effects: Let
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 [
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>> && ..) && (sized_range<maybe-const<Const, Fs>> && ..)
friend constexpr decltype(auto) iter_move(const iterator& it) noexcept(see below);
Effects: Equivalent to: return std::visit([](const auto& i) -> concat-rvalue-reference-t<maybe-const<Const, Views>.> { return ranges::iter_move(i); }, it.it_);
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>.>) && ..)
friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires see below;
Preconditions: x.it_.valueless_by_exception() and y.it_.valueless_by_exception() are each false.
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_);
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 type const iterator_t<maybe-const<Const, Views>> respectively.
The expression in the requires-clause is equivalent to swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> && (. && indirectly_swappable<iterator_t<maybe-const<Const, Views>>)

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant