26 Ranges library [ranges]

26.7 Range adaptors [range.adaptors]

26.7.31 Cartesian product view [range.cartesian]

26.7.31.1 Overview [range.cartesian.overview]

cartesian_­product_­view takes any non-zero number of ranges n and produces a view of tuples calculated by the n-ary cartesian product of the provided ranges.
The name views​::​cartesian_­product denotes a customization point object ([customization.point.object]).
Given a pack of subexpressions Es, the expression views​::​cartesian_­product(Es...) is expression-equivalent to
  • views​::​single(tuple()) if Es is an empty pack,
  • otherwise, cartesian_­product_­view<views​::​all_­t<decltype((Es))>...>(Es...).
[Example 1: vector<int> v { 0, 1, 2 }; for (auto&& [a, b, c] : views::cartesian_product(v, v, v)) { cout << a << ' ' << b << ' ' << c << '\n'; } // The above prints // 0 0 0 // 0 0 1 // 0 0 2 // 0 1 0 // 0 1 1 // ... — end example]

26.7.31.2 Class template cartesian_­product_­view [range.cartesian.view]

namespace std::ranges { template<bool Const, class First, class... Vs> concept cartesian-product-is-random-access = // exposition only (random_­access_­range<maybe-const<Const, First>> && ... && (random_­access_­range<maybe-const<Const, Vs>> && sized_­range<maybe-const<Const, Vs>>)); template<class R> concept cartesian-product-common-arg = // exposition only common_­range<R> || (sized_­range<R> && random_­access_­range<R>); template<bool Const, class First, class... Vs> concept cartesian-product-is-bidirectional = // exposition only (bidirectional_­range<maybe-const<Const, First>> && ... && (bidirectional_­range<maybe-const<Const, Vs>> && cartesian-product-common-arg<maybe-const<Const, Vs>>)); template<class First, class... Vs> concept cartesian-product-is-common = // exposition only cartesian-product-common-arg<First>; template<class... Vs> concept cartesian-product-is-sized = // exposition only (sized_­range<Vs> && ...); template<bool Const, template<class> class FirstSent, class First, class... Vs> concept cartesian-is-sized-sentinel = // exposition only (sized_­sentinel_­for<FirstSent<maybe-const<Const, First>>, iterator_t<maybe-const<Const, First>>> && ... && (sized_­range<maybe-const<Const, Vs>> && sized_­sentinel_­for<iterator_t<maybe-const<Const, Vs>>, iterator_t<maybe-const<Const, Vs>>>)); template<cartesian-product-common-arg R> constexpr auto cartesian-common-arg-end(R& r) { // exposition only if constexpr (common_­range<R>) { return ranges::end(r); } else { return ranges::begin(r) + ranges::distance(r); } } template<input_­range First, forward_­range... Vs> requires (view<First> && ... && view<Vs>) class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> { private: tuple<First, Vs...> bases_; // exposition only // [ranges.cartesian.iterator], class template cartesian_­product_­view​::​iterator template<bool Const> class iterator; // exposition only public: constexpr cartesian_product_view() = default; constexpr explicit cartesian_product_view(First first_base, Vs... bases); constexpr iterator<false> begin() requires (!simple-view<First> || ... || !simple-view<Vs>); constexpr iterator<true> begin() const requires (range<const First> && ... && range<const Vs>); constexpr iterator<false> end() requires ((!simple-view<First> || ... || !simple-view<Vs>) && cartesian-product-is-common<First, Vs...>); constexpr iterator<true> end() const requires cartesian-product-is-common<const First, const Vs...>; constexpr default_sentinel_t end() const noexcept; constexpr see below size() requires cartesian-product-is-sized<First, Vs...>; constexpr see below size() const requires cartesian-product-is-sized<const First, const Vs...>; }; template<class... Vs> cartesian_product_view(Vs&&...) -> cartesian_product_view<all_t<Vs>...>; }
constexpr explicit cartesian_product_view(First first_base, Vs... bases);
Effects: Initializes bases_­ with std​::​move(first_­base), std​::​move(bases)....
constexpr iterator<false> begin() requires (!simple-view<First> || ... || !simple-view<Vs>);
Effects: Equivalent to: return iterator<false>(tuple-transform(ranges​::​begin, bases_­));
constexpr iterator<true> begin() const requires (range<const First> && ... && range<const Vs>);
Effects: Equivalent to: return iterator<true>(tuple-transform(ranges​::​begin, bases_­));
constexpr iterator<false> end() requires ((!simple-view<First> || ... || !simple-view<Vs>) && cartesian-product-is-common<First, Vs...>); constexpr iterator<true> end() const requires cartesian-product-is-common<const First, const Vs...>;
Let:
  • is-const be true for the const-qualified overload, and false otherwise;
  • is-empty be true if the expression ranges​::​empty(rng) is true for any rng among the underlying ranges except the first one and false otherwise; and
  • begin-or-first-end(rng) be expression-equivalent to is-empty ? ranges​::​begin(rng) : cartesian-common-arg-end(rng) if rng is the first underlying range and ranges​::​begin(rng) otherwise.
Effects: Equivalent to: iterator<is-const> it(tuple-transform( [](auto& rng){ return begin-or-first-end(rng); }, bases_)); return it;
constexpr default_sentinel_t end() const noexcept;
Returns: default_­sentinel.
constexpr see below size() requires cartesian-product-is-sized<First, Vs...>; constexpr see below size() const requires cartesian-product-is-sized<const First, const Vs...>;
The return type is an implementation-defined unsigned-integer-like type.
Recommended practice: The return type should be the smallest unsigned-integer-like type that is sufficiently wide to store the product of the maximum sizes of all the underlying ranges, if such a type exists.
Let p be the product of the sizes of all the ranges in bases_­.
Preconditions: p can be represented by the return type.
Returns: p.

26.7.31.3 Class template cartesian_­product_­view​::​iterator [ranges.cartesian.iterator]

namespace std::ranges { template<input_­range First, forward_­range... Vs> requires (view<First> && ... && view<Vs>) template<bool Const> class cartesian_product_view<First, Vs...>::iterator { public: using iterator_category = input_iterator_tag; using iterator_concept = see below; using value_type = tuple<range_value_t<maybe-const<Const, First>>, range_value_t<maybe-const<Const, Vs>>...>; using reference = tuple<range_reference_t<maybe-const<Const, First>>, range_reference_t<maybe-const<Const, Vs>>...>; using difference_type = see below; iterator() requires forward_­range<maybe-const<Const, First>> = default; constexpr iterator(iterator<!Const> i) requires Const && (convertible_­to<iterator_t<First>, iterator_t<const First>> && ... && convertible_­to<iterator_t<Vs>, iterator_t<const Vs>>); constexpr auto operator*() const; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int) requires forward_­range<maybe-const<Const, First>>; constexpr iterator& operator--() requires cartesian-product-is-bidirectional<Const, First, Vs...>; constexpr iterator operator--(int) requires cartesian-product-is-bidirectional<Const, First, Vs...>; constexpr iterator& operator+=(difference_type x) requires cartesian-product-is-random-access<Const, First, Vs...>; constexpr iterator& operator-=(difference_type x) requires cartesian-product-is-random-access<Const, First, Vs...>; constexpr reference operator[](difference_type n) const requires cartesian-product-is-random-access<Const, First, Vs...>; friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<maybe-const<Const, First>>>; friend constexpr bool operator==(const iterator& x, default_sentinel_t); friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires all-random-access<Const, First, Vs...>; friend constexpr iterator operator+(const iterator& x, difference_type y) requires cartesian-product-is-random-access<Const, First, Vs...>; friend constexpr iterator operator+(difference_type x, const iterator& y) requires cartesian-product-is-random-access<Const, First, Vs...>; friend constexpr iterator operator-(const iterator& x, difference_type y) requires cartesian-product-is-random-access<Const, First, Vs...>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires cartesian-is-sized-sentinel<Const, iterator_t, First, Vs...>; friend constexpr difference_type operator-(iterator i, default_sentinel_t) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>; friend constexpr difference_type operator-(default_sentinel_t, iterator i) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>; 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, First>>> && ... && indirectly_­swappable<iterator_t<maybe-const<Const, Vs>>>); private: maybe-const<Const, cartesian_product_view>* parent_ = nullptr; // exposition only tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current_; // exposition only template<size_t N = sizeof...(Vs)> constexpr void next(); // exposition only template<size_t N = sizeof...(Vs)> constexpr void prev(); // exposition only template<class Tuple> constexpr difference_type distance-from(Tuple t); // exposition only constexpr explicit iterator(tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current); // exposition only }; }
iterator​::​iterator_­concept is defined as follows:
iterator​::​difference_­type is an implementation-defined signed-integer-like type.
Recommended practice: iterator​::​difference_­type should be the smallest signed-integer-like type that is sufficiently wide to store the product of the maximum sizes of all underlying ranges if such a type exists.
template<size_t N = sizeof...(Vs)> constexpr void next();
Effects: Equivalent to: auto& it = std::get<N>(current_); ++it; if constexpr (N > 0) { if (it == ranges::end(std::get<N>(parent_->bases_))) { it = ranges::begin(std::get<N>(parent_->bases_)); next<N - 1>(); } }
template<size_t N = sizeof...(Vs)> constexpr void prev();
Effects: Equivalent to: auto& it = std::get<N>(current_); if (it == ranges::begin(std::get<N>(parent_->bases_))) { it = cartesian-common-arg-end(std::get<N>(parent_->bases_)); if constexpr (N > 0) { prev<N - 1>(); } } --it;
template<class Tuple> constexpr difference_type distance-from(Tuple t);
Let:
  • scaled-size(N) be the product of static_­cast<difference_­type>(ranges​::​size(std​::​get<N>(parent_­->bases_­))) and if , otherwise static_­cast<difference_­type>(1);
  • scaled-distance(N) be the product of static_­cast<difference_­type>(std​::​get<N>(current_­) - std​::​get<N>(t)) and ; and
  • scaled-sum be the sum of scaled-distance(N) for every integer .
Preconditions: scaled-sum can be represented by difference_­type.
Returns: scaled-sum.
constexpr explicit iterator(tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current);
Effects: Initializes current_­ with std​::​move(current).
constexpr iterator(iterator<!Const> i) requires Const && (convertible_­to<iterator_t<First>, iterator_t<const First>> && ... && convertible_­to<iterator_t<Vs>, iterator_t<const Vs>>);
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: next(); return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
constexpr iterator operator++(int) requires forward_­range<maybe-const<Const, First>>;
Effects: Equivalent to: auto tmp = *this; ++*this; return tmp;
constexpr iterator& operator--() requires cartesian-product-is-bidirectional<Const, First, Vs...>;
Effects: Equivalent to: prev(); return *this;
constexpr iterator operator--(int) requires cartesian-product-is-bidirectional<Const, First, Vs...>;
Effects: Equivalent to: auto tmp = *this; --*this; return tmp;
constexpr iterator& operator+=(difference_type x) requires cartesian-product-is-random-access<Const, First, Vs...>;
Let orig be the value of *this before the call.
Let ret be:
  • If x > 0, the value of *this had next been called x times.
  • Otherwise, if x < 0, the value of *this had prev been called -x times.
  • Otherwise, orig.
Preconditions: x is in the range [ranges​::​distance(*this, ranges​::​begin(*parent_­)),
ranges​::​distance(*this, ranges​::​end(*parent_­))].
Effects: Sets the value of *this to ret.
Returns: *this.
Complexity: Constant.
constexpr iterator& operator-=(difference_type x) requires cartesian-product-is-random-access<Const, First, Vs...>;
Effects: Equivalent to: *this += -x; return *this;
constexpr reference operator[](difference_type n) const requires cartesian-product-is-random-access<Const, First, Vs...>;
Effects: Equivalent to: return *((*this) + n);
friend constexpr bool operator==(const iterator& x, const iterator& y) requires equality_­comparable<iterator_t<maybe-const<Const, First>>>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator==(const iterator& x, default_sentinel_t);
Returns: true if std​::​get<i>(x.current_­) == ranges​::​end(std​::​get<i>(x.parent_­->bases_­)) is true for any integer ; otherwise, false.
friend constexpr auto operator<=>(const iterator& x, const iterator& y) requires all-random-access<Const, First, Vs...>;
Effects: Equivalent to: return x.current_­ <=> y.current_­;
friend constexpr iterator operator+(const iterator& x, difference_type y) requires cartesian-product-is-random-access<Const, First, Vs...>;
Effects: Equivalent to: return iterator(x) += y;
friend constexpr iterator operator+(difference_type x, const iterator& y) requires cartesian-product-is-random-access<Const, First, Vs...>;
Effects: Equivalent to: return y + x;
friend constexpr iterator operator-(const iterator& x, difference_type y) requires cartesian-product-is-random-access<Const, First, Vs...>;
Effects: Equivalent to: return iterator(x) -= y;
friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires cartesian-is-sized-sentinel<Const, iterator_t, First, Vs...>;
Effects: Equivalent to: return x.distance-from(y.current_­);
friend constexpr difference_type operator-(iterator i, default_sentinel_t) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>;
Let end-tuple be an object of a type that is a specialization of tuple, such that:
  • std​::​get<0>(end-tuple) has the same value as ranges​::​end(std​::​get<0>(i.parent_­->bases_­));
  • std​::​get<N>(end-tuple) has the same value as ranges​::​begin(std​::​get<N>(i.parent_­->bases_­)) for every integer .
Effects: Equivalent to: return i.distance-from(end-tuple);
friend constexpr difference_type operator-(default_sentinel_t s, iterator i) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>;
Effects: Equivalent to: return -(i - s);
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 the logical and of the following expressions:
  • noexcept(ranges​::​iter_­move(std​::​get<N>(i.current_­))) for every integer
    ,
  • is_­nothrow_­move_­constructible_­v<range_­rvalue_­reference_­t<maybe-const<Const, T>>>
    for every type T in First, Vs....
friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below) requires (indirectly_­swappable<iterator_t<maybe-const<Const, First>>> && ... && indirectly_­swappable<iterator_t<maybe-const<Const, Vs>>>);
Effects: For every integer , performs: ranges::iter_swap(std::get<i>(l.current_), std::get<i>(r.current_))
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 .