#
zip_view takes any number of views and produces a view of tuples of references to the corresponding elements of the constituent views.
2
#
The name views​::​zip denotes a customization point object ([customization.point.object]).
Given a pack of subexpressions Es.., the expression views​::​zip(Es..) is expression-equivalent to
  • auto(views​::​empty<tuple<>) if Es is an empty pack,
  • otherwise, zip_view<views​::​all_t<decltype(Es))>.>(Es..).
[Example 1: vector v = {1, 2}; list l = {'a', 'b', 'c'}; auto z = views::zip(v, l); range_reference_t<decltype(z)> f = z.front(); / f is a tuple<int&, char&> / that refers to the first element of v and l for (auto& [x, y] : z) { cout << '(' << x << ", " << y << ") "; / prints (1, a) (2, b) } — end example]

25.7.25.2 Class template zip_view [range.zip.view]

namespace std::ranges { template<class. Rs> concept zip-is-common = / exposition only (sizeof.(Rs) == 1 && (common_range<Rs> && ..) || (!(bidirectional_range<Rs> && ..) && (common_range<Rs> && ..) || ((random_access_range<Rs> && ..) && (sized_range<Rs> && ..); template<input_range.. Views> requires (view<Views> && ..) && (sizeof.(Views) > 0) class zip_view : public view_interface<zip_view<Views..> { tuple<Views..> views_; / exposition only / [range.zip.iterator], class template zip_view​::​iterator template<bool> class iterator; / exposition only / [range.zip.sentinel], class template zip_view​::​sentinel template<bool> class sentinel; / exposition only public: zip_view() = default; constexpr explicit zip_view(Views.. views); constexpr auto begin() requires (!(simple-view<Views> && ..) { return iterator<false>(tuple-transform(ranges::begin, views_)); } constexpr auto begin() const requires (range<const Views> && ..) { return iterator<true>(tuple-transform(ranges::begin, views_)); } constexpr auto end() requires (!(simple-view<Views> && ..) { if constexpr (!zip-is-common<Views..>) { return sentinel<false>(tuple-transform(ranges::end, views_)); } else if constexpr ((random_access_range<Views> && ..) { return begin() + iter_difference_t<iterator<false>(size(); } else { return iterator<false>(tuple-transform(ranges::end, views_)); } } constexpr auto end() const requires (range<const Views> && ..) { if constexpr (!zip-is-common<const Views..>) { return sentinel<true>(tuple-transform(ranges::end, views_)); } else if constexpr ((random_access_range<const Views> && ..) { return begin() + iter_difference_t<iterator<true>(size(); } else { return iterator<true>(tuple-transform(ranges::end, views_)); } } constexpr auto size() requires (sized_range<Views> && ..); constexpr auto size() const requires (sized_range<const Views> && ..); }; template<class. Rs> zip_view(Rs&&.) -> zip_view<views::all_t<Rs>.>; }
Two zip_view objects have the same underlying sequence if and only if the corresponding elements of views_ are equal ([concepts.equality]) and have the same underlying sequence.
[Note 1: 
In particular, comparison of iterators obtained from zip_view objects that do not have the same underlying sequence is not required to produce meaningful results ([iterator.concept.forward]).
— end note]
constexpr explicit zip_view(Views.. views);
Effects: Initializes views_ with std​::​move(views)..
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 ranges::min({CT(sizes).}); }, tuple-transform(ranges::size, views_));

25.7.25.3 Class template zip_view​::​iterator [range.zip.iterator]

namespace std::ranges { template<input_range.. Views> requires (view<Views> && ..) && (sizeof.(Views) > 0) template<bool Const> class zip_view<Views..>::iterator { tuple<iterator_t<maybe-const<Const, Views>>.> current_; / exposition only constexpr explicit iterator(tuple<iterator_t<maybe-const<Const, Views>>.>); / exposition only public: using iterator_category = input_iterator_tag; / not always present using iterator_concept = see below; using value_type = tuple<range_value_t<maybe-const<Const, Views>>.>; using difference_type = common_type_t<range_difference_t<maybe-const<Const, Views>>.>; iterator() = default; constexpr iterator(iterator<!Const> i) requires Const && (convertible_to<iterator_t<Views>, iterator_t<const Views>> && ..); constexpr auto operator*() const; constexpr iterator& operator+(); constexpr void operator+(int); constexpr iterator operator+(int) requires all-forward<Const, Views..>; constexpr iterator& operator-() requires all-bidirectional<Const, Views..>; constexpr iterator operator-(int) requires all-bidirectional<Const, Views..>; constexpr iterator& operator+=(difference_type x) requires all-random-access<Const, Views..>; constexpr iterator& operator-=(difference_type x) requires all-random-access<Const, Views..>; constexpr auto operator[](difference_type n) const requires all-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 auto operator<=>(const iterator& x, const iterator& y) requires all-random-access<Const, Views..>; friend constexpr iterator operator+(const iterator& i, difference_type n) requires all-random-access<Const, Views..>; friend constexpr iterator operator+(difference_type n, const iterator& i) requires all-random-access<Const, Views..>; friend constexpr iterator operator-(const iterator& i, difference_type n) requires all-random-access<Const, Views..>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires (sized_sentinel_for<iterator_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>> && ..); friend constexpr auto iter_move(const iterator& i) noexcept(see below); friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below) requires (indirectly_swappable<iterator_t<maybe-const<Const, Views>> && ..); }; }
iterator​::​iterator_concept is defined as follows:
  • If all-random-access<Const, Views..> is modeled, then iterator_concept denotes random_access_iterator_tag.
  • Otherwise, if all-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.
iterator​::​iterator_category is present if and only if all-forward<Const, Views..> is modeled.
If the invocation of any non-const member function of iterator exits via an exception, the iterator acquires a singular value.
constexpr explicit iterator(tuple<iterator_t<maybe-const<Const, Views>>.> current);
Effects: Initializes current_ with std​::​move(current).
constexpr iterator(iterator<!Const> i) requires Const && (convertible_to<iterator_t<Views>, iterator_t<const Views>> && ..);
Effects: Initializes current_ with std​::​move(i.current_).
constexpr auto operator*() const;
Effects: Equivalent to: return tuple-transform([](auto& i) -> decltype(auto) { return *i; }, current_);
constexpr iterator& operator+();
Effects: Equivalent to: tuple-for-each([](auto& i) { ++i; }, current_); 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 all-bidirectional<Const, Views..>;
Effects: Equivalent to: tuple-for-each([](auto& i) { --i; }, current_); return *this;
constexpr iterator operator-(int) requires all-bidirectional<Const, Views..>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
constexpr iterator& operator+=(difference_type x) requires all-random-access<Const, Views..>;
Effects: Equivalent to: tuple-for-each([&]<class I>(I& i) { i += iter_difference_t<I>(x); }, current_); return *this;
constexpr iterator& operator-=(difference_type x) requires all-random-access<Const, Views..>;
Effects: Equivalent to: tuple-for-each([&]<class I>(I& i) { i -= iter_difference_t<I>(x); }, current_); return *this;
constexpr auto operator[](difference_type n) const requires all-random-access<Const, Views..>;
Effects: Equivalent to: return tuple-transform([&]<class I>(I& i) -> decltype(auto) { return i[iter_difference_t<I>(n)]; }, current_);
friend constexpr bool operator=(const iterator& x, const iterator& y) requires (equality_comparable<iterator_t<maybe-const<Const, Views>> && ..);
Returns:
  • x.current_ == y.current_ if all-bidirectional<Const, Views..> is true.
  • Otherwise, true if there exists an integer
    [Note 1: 
    This allows zip_view to model common_range when all constituent views model common_range.
    — end note]
  • Otherwise, false.
friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires all-random-access<Const, Views..>;
Returns: x.current_ <=> y.current_.
friend constexpr iterator operator+(const iterator& i, difference_type n) requires all-random-access<Const, Views..>; friend constexpr iterator operator+(difference_type n, const iterator& i) requires all-random-access<Const, Views..>;
Effects: Equivalent to: auto r = i; r += n; return r;
friend constexpr iterator operator-(const iterator& i, difference_type n) requires all-random-access<Const, Views..>;
Effects: Equivalent to: auto r = i; r -= n; return r;
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires (sized_sentinel_for<iterator_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>> && ..);
Returns: The value with the smallest absolute value among DIST(n) for all integers
friend constexpr auto iter_move(const iterator& i) noexcept(see below);
Effects: Equivalent to: return tuple-transform(ranges::iter_move, i.current_);
Remarks: The exception specification is equivalent to: (noexcept(ranges::iter_move(declval<const iterator_t<maybe-const<Const, Views>>&>() && ..) && (is_nothrow_move_constructible_v<range_rvalue_reference_t<maybe-const<Const, Views>> && ..)
friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below) requires (indirectly_swappable<iterator_t<maybe-const<Const, Views>> && ..);
Effects: For every integer
Remarks: The exception specification is equivalent to the logical and of the following expressions: noexcept(ranges::iter_swap(std::get<i>(l.current_), std::get<i>(r.current_)) for every integer

25.7.25.4 Class template zip_view​::​sentinel [range.zip.sentinel]

namespace std::ranges { template<input_range.. Views> requires (view<Views> && ..) && (sizeof.(Views) > 0) template<bool Const> class zip_view<Views..>::sentinel { tuple<sentinel_t<maybe-const<Const, Views>>.> end_; / exposition only constexpr explicit sentinel(tuple<sentinel_t<maybe-const<Const, Views>>.> end); / exposition only public: sentinel() = default; constexpr sentinel(sentinel<!Const> i) requires Const && (convertible_to<sentinel_t<Views>, sentinel_t<const Views>> && ..); template<bool OtherConst> requires (sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr bool operator=(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>.> operator-(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>.> operator-(const sentinel& y, const iterator<OtherConst>& x); }; }
constexpr explicit sentinel(tuple<sentinel_t<maybe-const<Const, Views>>.> end);
Effects: Initializes end_ with end.
constexpr sentinel(sentinel<!Const> i) requires Const && (convertible_to<sentinel_t<Views>, sentinel_t<const Views>> && ..);
Effects: Initializes end_ with std​::​move(i.end_).
template<bool OtherConst> requires (sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr bool operator=(const iterator<OtherConst>& x, const sentinel& y);
Returns: true if there exists an integer
Otherwise, false.
template<bool OtherConst> requires (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>.> operator-(const iterator<OtherConst>& x, const sentinel& y);
Let D be the return type.
Let DIST(i) be D(std​::​get<i>(x.current_) - std​::​get<i>(y.end_).
Returns: The value with the smallest absolute value among DIST(n) for all integers
template<bool OtherConst> requires (sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>> && ..) friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>.> operator-(const sentinel& y, const iterator<OtherConst>& x);
Effects: Equivalent to: return -(x - y);

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