24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

24.7.19 Zip view [range.zip]

24.7.19.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<class... Ts> using tuple-or-pair = see below; // exposition only template<class F, class Tuple> constexpr auto tuple-transform(F&& f, Tuple&& tuple) { // exposition only return apply([&]<class... Ts>(Ts&&... elements) { return tuple-or-pair<invoke_result_t<F&, Ts>...>( invoke(f, std::forward<Ts>(elements))... ); }, std::forward<Tuple>(tuple)); } template<class F, class Tuple> constexpr void tuple-for-each(F&& f, Tuple&& tuple) { // exposition only apply([&]<class... Ts>(Ts&&... elements) { (invoke(f, std::forward<Ts>(elements)), ...); }, std::forward<Tuple>(tuple)); } 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>...>; }
Given some pack of types Ts, the alias template tuple-or-pair is defined as follows:
  • If sizeof...(Ts) is 2, tuple-or-pair<Ts...> denotes pair<Ts...>.
  • Otherwise, tuple-or-pair<Ts...> denotes tuple<Ts...>.
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_));