Files
2025-10-25 03:02:53 +03:00

28 KiB
Raw Permalink Blame History

[range.zip]

25 Ranges library [ranges]

25.7 Range adaptors [range.adaptors]

25.7.25 Zip view [range.zip]

25.7.25.1 Overview [range.zip.overview]

1

#

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 lfor (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 && ...)) ||(!(bidirectional_range && ...) && (common_range && ...)) ||((random_access_range && ...) && (sized_range && ...)); template<input_range... Views>requires (view && ...) && (sizeof...(Views) > 0)class zip_view : public view_interface<zip_view<Views...>> { tuple<Views...> views_; // exposition only// [range.zip.iterator], class template zip_view::iteratortemplate class iterator; // exposition only// [range.zip.sentinel], class template zip_view::sentineltemplate class sentinel; // exposition onlypublic: zip_view() = default; constexpr explicit zip_view(Views... views); constexpr auto begin() requires (!(simple-view && ...)) {return iterator(tuple-transform(ranges::begin, views_)); }constexpr auto begin() const requires (range && ...) {return iterator(tuple-transform(ranges::begin, views_)); }constexpr auto end() requires (!(simple-view && ...)) {if constexpr (zip-is-common<Views...>) {return sentinel(tuple-transform(ranges::end, views_)); } else if constexpr ((random_access_range && ...)) {return begin() + iter_difference_t<iterator>(size()); } else {return iterator(tuple-transform(ranges::end, views_)); }}constexpr auto end() const requires (range && ...) {if constexpr (zip-is-common) {return sentinel(tuple-transform(ranges::end, views_)); } else if constexpr ((random_access_range && ...)) {return begin() + iter_difference_t<iterator>(size()); } else {return iterator(tuple-transform(ranges::end, views_)); }}constexpr auto size() requires (sized_range && ...); constexpr auto size() const requires (sized_range && ...); }; template<class... Rs> zip_view(Rs&&...) -> zip_view<views::all_t...>;}

1

#

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

2

#

Effects: Initializes views_ with std::move(views)....

🔗

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

3

#

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 && ...) && (sizeof...(Views) > 0)templateclass zip_view<Views...>::iterator { tuple<iterator_t<maybe-const<Const, Views>>...> current_; // exposition onlyconstexpr explicit iterator(tuple<iterator_t<maybe-const<Const, Views>>...>); // exposition onlypublic:using iterator_category = input_iterator_tag; // not always presentusing 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 i)requires Const && (convertible_to<iterator_t, iterator_t> && ...); 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) constrequires 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>>> && ...); };}

1

#

iterator::iterator_concept is defined as follows:

  • (1.1)

    If all-random-access<Const, Views...> is modeled, then iterator_concept denotes random_access_iterator_tag.

  • (1.2)

    Otherwise, if all-bidirectional<Const, Views...> is modeled, then iterator_concept denotes bidirectional_iterator_tag.

  • (1.3)

    Otherwise, if all-forward<Const, Views...> is modeled, then iterator_concept denotes forward_iterator_tag.

  • (1.4)

    Otherwise, iterator_concept denotes input_iterator_tag.

2

#

iterator::iterator_category is present if and only if all-forward<Const, Views...> is modeled.

3

#

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

4

#

Effects: Initializes current_ with std::move(current).

🔗

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

5

#

Effects: Initializes current_ with std::move(i.current_).

🔗

constexpr auto operator*() const;

6

#

Effects: Equivalent to:return tuple-transform([](auto& i) -> decltype(auto) { return *i; }, current_);

🔗

constexpr iterator& operator++();

7

#

Effects: Equivalent to:tuple-for-each([](auto& i) { ++i; }, current_);return *this;

🔗

constexpr void operator++(int);

8

#

Effects: Equivalent to ++*this.

🔗

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

9

#

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

🔗

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

10

#

Effects: Equivalent to:tuple-for-each([](auto& i) { --i; }, current_);return *this;

🔗

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

11

#

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

🔗

constexpr iterator& operator+=(difference_type x) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

12

#

Effects: Equivalent to:tuple-for-each([&](I& i) { i += iter_difference_t(x); }, current_);return *this;

🔗

constexpr iterator& operator-=(difference_type x) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

13

#

Effects: Equivalent to:tuple-for-each([&](I& i) { i -= iter_difference_t(x); }, current_);return *this;

🔗

constexpr auto operator[](difference_type n) const requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

14

#

Effects: Equivalent to:return tuple-transform([&](I& i) -> decltype(auto) {return i[iter_difference_t(n)];}, current_);

🔗

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

15

#

Returns:

  • (15.1)

    x.current_ == y.current_ if all-bidirectional<Const, Views...> is true.

  • (15.2)

    Otherwise, true if there exists an integer 0≤i<sizeof...(Views) such that bool(std::get(x.current_) == std::get(y.current_)) is true. [Note 1: This allows zip_view to model common_range when all constituent views model common_range. — end note]

  • (15.3)

    Otherwise, false.

🔗

friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

16

#

Returns: x.current_ <=> y.current_.

🔗

friend constexpr iterator operator+(const iterator& i, difference_type n) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>; friend constexpr iterator operator+(difference_type n, const iterator& i) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

17

#

Effects: Equivalent to:auto r = i; r += n;return r;

🔗

friend constexpr iterator operator-(const iterator& i, difference_type n) requires [all-random-access](range.adaptor.helpers#concept:all-random-access "25.7.5Range adaptor helpers[range.adaptor.helpers]")<Const, Views...>;

18

#

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.concept.sizedsentinel#concept:sized_sentinel_for "24.3.4.8Concept sized_­sentinel_­for[iterator.concept.sizedsentinel]")<iterator_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>>> && ...);

19

#

Let DIST(i) be difference_type(std::get(x.current_) - std::get(y.current_)).

20

#

Returns: The value with the smallest absolute value among DIST(n) for all integers 0≤n<sizeof...(Views).

🔗

friend constexpr auto iter_move(const iterator& i) noexcept(see below);

21

#

Effects: Equivalent to:return tuple-transform(ranges::iter_move, i.current_);

22

#

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](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4Concept indirectly_­swappable[alg.req.ind.swap]")<iterator_t<maybe-const<Const, Views>>> && ...);

23

#

Effects: For every integer 0≤i<sizeof...(Views), performs:ranges::iter_swap(std::get(l.current_), std::get(r.current_))

24

#

Remarks: The exception specification is equivalent to the logical and of the following expressions:noexcept(ranges::iter_swap(std::get(l.current_), std::get(r.current_))) for every integer 0≤i<sizeof...(Views).

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

🔗

namespace std::ranges {template<input_range... Views>requires (view && ...) && (sizeof...(Views) > 0)templateclass zip_view<Views...>::sentinel { tuple<sentinel_t<maybe-const<Const, Views>>...> end_; // exposition onlyconstexpr explicit sentinel(tuple<sentinel_t<maybe-const<Const, Views>>...> end); // exposition onlypublic:sentinel() = default; constexpr sentinel(sentinel i)requires Const && (convertible_to<sentinel_t, sentinel_t> && ...); templaterequires (sentinel_for<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>>> && ...)friend constexpr bool operator==(const iterator& x, const sentinel& y); templaterequires (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& x, const sentinel& y); templaterequires (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& x); };}

🔗

constexpr explicit sentinel(tuple<sentinel_t<maybe-const<Const, Views>>...> end);

1

#

Effects: Initializes end_ with end.

🔗

constexpr sentinel(sentinel<!Const> i) requires Const && ([convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<sentinel_t<Views>, sentinel_t<const Views>> && ...);

2

#

Effects: Initializes end_ with std::move(i.end_).

🔗

template<bool OtherConst> requires ([sentinel_for](iterator.concept.sentinel#concept:sentinel_for "24.3.4.7Concept sentinel_­for[iterator.concept.sentinel]")<sentinel_t<maybe-const<Const, Views>>, iterator_t<maybe-const<OtherConst, Views>>> && ...) friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);

3

#

Returns: true if there exists an integer 0≤i<sizeof...(Views) such that bool(std::get(x.current_) == std::get(y.end_)) is true.

Otherwise, false.

🔗

template<bool OtherConst> requires ([sized_sentinel_for](iterator.concept.sizedsentinel#concept:sized_sentinel_for "24.3.4.8Concept sized_­sentinel_­for[iterator.concept.sizedsentinel]")<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);

4

#

Let D be the return type.

Let DIST(i) beD(std::get(x.current_) - std::get(y.end_)).

5

#

Returns: The value with the smallest absolute value among DIST(n) for all integers 0≤n<sizeof...(Views).

🔗

template<bool OtherConst> requires ([sized_sentinel_for](iterator.concept.sizedsentinel#concept:sized_sentinel_for "24.3.4.8Concept sized_­sentinel_­for[iterator.concept.sizedsentinel]")<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);

6

#

Effects: Equivalent to: return -(x - y);