Files
cppdraft_translate/cppdraft/range/cartesian.md
2025-10-25 03:02:53 +03:00

37 KiB
Raw Blame History

[range.cartesian]

25 Ranges library [ranges]

25.7 Range adaptors [range.adaptors]

25.7.33 Cartesian product view [range.cartesian]

25.7.33.1 Overview [range.cartesian.overview]

1

#

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.

2

#

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

3

#

[Example 1: vector 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]

25.7.33.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>>)); templateconcept cartesian-product-common-arg = // exposition onlycommon_range || (sized_range && random_access_range); 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...>concept cartesian-product-is-common = // exposition onlycartesian-product-common-arg; template<class... Vs>concept cartesian-product-is-sized = // exposition only(sized_range && ...); template<bool Const, template 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 onlyif constexpr (common_range) {return ranges::end(r); } else {return ranges::begin(r) + ranges::distance(r); }}template<input_range First, forward_range... Vs>requires (view && ... && view)class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> {private: tuple<First, Vs...> bases_; // exposition only// [range.cartesian.iterator], class template cartesian_product_view::iteratortemplate class iterator; // exposition onlypublic:constexpr cartesian_product_view() = default; constexpr explicit cartesian_product_view(First first_base, Vs... bases); constexpr iterator begin()requires (simple-view || ... || simple-view); constexpr iterator begin() constrequires (range && ... && range); constexpr iterator end()requires ((simple-view || ... || simple-view) &&cartesian-product-is-common<First, Vs...>); constexpr iterator end() constrequires 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() constrequires cartesian-product-is-sized<const First, const Vs...>; }; template<class... Vs> cartesian_product_view(Vs&&...) -> cartesian_product_view<views::all_t...>;}

🔗

constexpr explicit cartesian_product_view(First first_base, Vs... bases);

1

#

Effects: Initializes bases_ with std::move(first_base), std::move(bases)....

🔗

constexpr iterator<false> begin() requires (![simple-view](range.utility.helpers#concept:simple-view "25.5.2Helper concepts[range.utility.helpers]")<First> || ... || ![simple-view](range.utility.helpers#concept:simple-view "25.5.2Helper concepts[range.utility.helpers]")<Vs>);

2

#

Effects: Equivalent to:return iterator(*this, tuple-transform(ranges::begin, bases_));

🔗

constexpr iterator<true> begin() const requires ([range](range.range#concept:range "25.4.2Ranges[range.range]")<const First> && ... && [range](range.range#concept:range "25.4.2Ranges[range.range]")<const Vs>);

3

#

Effects: Equivalent to:return iterator(*this, tuple-transform(ranges::begin, bases_));

🔗

constexpr iterator<false> end() requires ((![simple-view](range.utility.helpers#concept:simple-view "25.5.2Helper concepts[range.utility.helpers]")<First> || ... || ![simple-view](range.utility.helpers#concept:simple-view "25.5.2Helper concepts[range.utility.helpers]")<Vs>) && [cartesian-product-is-common](#concept:cartesian-product-is-common "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<First, Vs...>); constexpr iterator<true> end() const requires [cartesian-product-is-common](#concept:cartesian-product-is-common "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<const First, const Vs...>;

4

#

Let:

is-const be true for the const-qualified overload, andfalse otherwise;

is-empty be true if the expression ranges::empty(rng) is true for any rng among the underlying ranges except the first one andfalse otherwise; and

begin-or-first-end(rng) be expression-equivalent tois-empty ? ranges::begin(rng) : cartesian-common-arg-end(rng) if rng is the first underlying range andranges::begin(rng) otherwise.

5

#

Effects: Equivalent to:iterator<is-const> it(*this, tuple-transform([](auto& rng){ return begin-or-first-end(rng); }, bases_));return it;

🔗

constexpr default_sentinel_t end() const noexcept;

6

#

Returns: default_sentinel.

🔗

constexpr see below size() requires [cartesian-product-is-sized](#concept:cartesian-product-is-sized "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<First, Vs...>; constexpr see below size() const requires [cartesian-product-is-sized](#concept:cartesian-product-is-sized "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<const First, const Vs...>;

7

#

The return type is an implementation-defined unsigned-integer-like type.

8

#

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.

9

#

Let p be the product of the sizes of all the ranges in bases_.

10

#

Preconditions: p can be represented by the return type.

11

#

Returns: p.

25.7.33.3 Class template cartesian_product_view::iterator [range.cartesian.iterator]

🔗

namespace std::ranges {template<input_range First, forward_range... Vs>requires (view && ... && view)templateclass 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() = default; constexpr iterator(iterator i) requires Const &&(convertible_to<iterator_t, iterator_t> &&... && convertible_to<iterator_t, iterator_t>); 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) constrequires 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-(const iterator& i, default_sentinel_t)requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>; friend constexpr difference_type operator-(default_sentinel_t, const 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:using Parent = maybe-const<Const, cartesian_product_view>; // *exposition onlyParent parent_ = nullptr; // exposition only tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current_; // exposition onlytemplate<size_t N = sizeof...(Vs)>constexpr void next(); // exposition onlytemplate<size_t N = sizeof...(Vs)>constexpr void prev(); // exposition onlytemplateconstexpr difference_type distance-from(const Tuple& t) const; // exposition onlyconstexpr iterator(Parent& parent, tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current); // exposition only};}

1

#

iterator::iterator_concept is defined as follows:

2

#

iterator::difference_type is an implementation-defined signed-integer-like type.

3

#

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

4

#

Effects: Equivalent to:auto& it = std::get(current_);++it;if constexpr (N > 0) {if (it == ranges::end(std::get(parent_->bases_))) { it = ranges::begin(std::get(parent_->bases_)); next<N - 1>(); }}

🔗

template<size_t N = sizeof...(Vs)> constexpr void prev();

5

#

Effects: Equivalent to:auto& it = std::get(current_);if constexpr (N > 0) {if (it == ranges::begin(std::get(parent_->bases_))) { it = cartesian-common-arg-end(std::get(parent_->bases_)); prev<N - 1>(); }}--it;

🔗

template<class Tuple> constexpr difference_type distance-from(const Tuple& t) const;

6

#

Let:

scaled-size(N) be the product ofstatic_cast<difference_type>(ranges::size(std::get<N>(parent_->bases_))) andscaled-size(N+1) if N ≤ sizeof...(Vs), otherwise static_cast<difference_type>(1);

scaled-distance(N) be the product ofstatic_cast<difference_type>(std::get(current_) - std::get(t)) and scaled-size(N+1); and

scaled-sum be the sum of scaled-distance(N) for every integer 0 ≤ N ≤ sizeof...(Vs).

7

#

Preconditions: scaled-sum can be represented by difference_type.

8

#

Returns: scaled-sum.

🔗

constexpr iterator(Parent& parent, tuple<iterator_t<maybe-const<Const, First>>, iterator_t<maybe-const<Const, Vs>>...> current);

9

#

Effects: Initializesparent_ with addressof(parent) andcurrent_ 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<First>, iterator_t<const First>> && ... && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<iterator_t<Vs>, iterator_t<const Vs>>);

10

#

Effects: Initializesparent_ with i.parent_ andcurrent_ with std::move(i.current_).

🔗

constexpr auto operator*() const;

11

#

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

🔗

constexpr iterator& operator++();

12

#

Effects: Equivalent to:next();return *this;

🔗

constexpr void operator++(int);

13

#

Effects: Equivalent to ++*this.

🔗

constexpr iterator operator++(int) requires [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<maybe-const<Const, First>>;

14

#

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

🔗

constexpr iterator& operator--() requires [cartesian-product-is-bidirectional](#concept:cartesian-product-is-bidirectional "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

15

#

Effects: Equivalent to:prev();return *this;

🔗

constexpr iterator operator--(int) requires [cartesian-product-is-bidirectional](#concept:cartesian-product-is-bidirectional "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

16

#

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

🔗

constexpr iterator& operator+=(difference_type x) requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

17

#

Let orig be the value of *this before the call.

Let ret be:

  • (17.1)

    If x > 0, the value of *this had next been called x times.

  • (17.2)

    Otherwise, if x < 0, the value of *this had prev been called -x times.

  • (17.3)

    Otherwise, orig.

18

#

Preconditions: x is in the range[ranges::distance(this, ranges::begin(**parent_)),
ranges::distance(this, ranges::end(**parent_))].

19

#

Effects: Sets the value of *this to ret.

20

#

Returns: *this.

21

#

Complexity: Constant.

🔗

constexpr iterator& operator-=(difference_type x) requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

22

#

Effects: Equivalent to:*this += -x;return *this;

🔗

constexpr reference operator[](difference_type n) const requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

23

#

Effects: Equivalent to: return *((*this) + n);

🔗

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, First>>>;

24

#

Effects: Equivalent to: return x.current_ == y.current_;

🔗

friend constexpr bool operator==(const iterator& x, default_sentinel_t);

25

#

Returns: true if std::get(x.current_) == ranges::end(std::get(x.parent_->bases_)) is true for any integer 0 ≤ i ≤ sizeof...(Vs); 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, First, Vs...>;

26

#

Effects: Equivalent to: return x.current_ <=> y.current_;

🔗

friend constexpr iterator operator+(const iterator& x, difference_type y) requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

27

#

Effects: Equivalent to: return iterator(x) += y;

🔗

friend constexpr iterator operator+(difference_type x, const iterator& y) requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

28

#

Effects: Equivalent to: return y + x;

🔗

friend constexpr iterator operator-(const iterator& x, difference_type y) requires [cartesian-product-is-random-access](#concept:cartesian-product-is-random-access "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, First, Vs...>;

29

#

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

🔗

friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires [cartesian-is-sized-sentinel](#concept:cartesian-is-sized-sentinel "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, iterator_t, First, Vs...>;

30

#

Effects: Equivalent to: return x.distance-from(y.current_);

🔗

friend constexpr difference_type operator-(const iterator& i, default_sentinel_t) requires [cartesian-is-sized-sentinel](#concept:cartesian-is-sized-sentinel "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, sentinel_t, First, Vs...>;

31

#

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 asranges::end(std::get<0>(i.parent_->bases_));

std::get(end-tuple) has the same value asranges::begin(std::get(i.parent_->bases_)) for every integer 1 ≤ N ≤ sizeof...(Vs).

32

#

Effects: Equivalent to: return i.distance-from(end-tuple);

🔗

friend constexpr difference_type operator-(default_sentinel_t s, const iterator& i) requires [cartesian-is-sized-sentinel](#concept:cartesian-is-sized-sentinel "25.7.33.2Class template cartesian_­product_­view[range.cartesian.view]")<Const, sentinel_t, First, Vs...>;

33

#

Effects: Equivalent to: return -(i - s);

🔗

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

34

#

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

35

#

Remarks: The exception specification is equivalent to the logical and of the following expressions:

noexcept(ranges::iter_move(std::get(i.current_))) for every integer
0 ≤ N ≤ sizeof...(Vs),

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

36

#

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

37

#

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...(Vs).