From b77c526dda683307f1c0048efa0d922eb5dd02e0 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 24 Jul 2022 20:40:31 +0200 Subject: [PATCH 1/2] P2374R4 views::cartesian_product --- source/macros.tex | 3 + source/ranges.tex | 819 ++++++++++++++++++++++++++++++++++++++++++++- source/support.tex | 1 + 3 files changed, 806 insertions(+), 17 deletions(-) diff --git a/source/macros.tex b/source/macros.tex index 84f6ecbf71..9f29cdd9ea 100644 --- a/source/macros.tex +++ b/source/macros.tex @@ -147,6 +147,8 @@ \let\textup\nocode% \let\otcode\tcode% \let\tcode\nocode% +\let\oexposid\exposid% +\let\exposid\nocode% \let\ogrammarterm\grammarterm% \let\grammarterm\nocode% \let\omname\mname% @@ -157,6 +159,7 @@ \let\BreakableUnderscore\textunderscore% \edef\x{#1}% \let\tcode\otcode% +\let\exposid\oexposid% \let\grammarterm\gterm% \let\mname\omname% \let\Cpp\oCpp% diff --git a/source/ranges.tex b/source/ranges.tex index e856c35bb2..e5791ca17f 100644 --- a/source/ranges.tex +++ b/source/ranges.tex @@ -478,7 +478,14 @@ template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; - namespace views { inline constexpr @\unspecnc@ stride = @\unspecnc@; } // freestanding + namespace views { inline constexpr @\unspecnc@ stride = @\unspecnc@; } + + // \ref{range.cartesian}, cartesian product view + template<@\libconcept{input_range}@ First, @\libconcept{forward_range}@... Vs> + requires (@\libconcept{view}@ && ... && @\libconcept{view}@) + class cartesian_product_view; + + namespace views { inline constexpr @\unspecnc@ cartesian_product = @\unspecnc@; } } namespace std { @@ -3764,6 +3771,41 @@ to temporarily cache values as it is iterated over. \end{note} +\rSec2[range.adaptor.tuple]{Range adaptor helpers} + +\begin{codeblock} +namespace std::ranges { + template + using @\exposid{tuple-or-pair}@ = @\seebelow@; // \expos + + template + constexpr auto @\exposid{tuple-transform}@(F&& f, Tuple&& tuple) { // \expos + return apply([&](Ts&&... elements) { + return @\exposid{tuple-or-pair}@...>( + invoke(f, std::forward(elements))... + ); + }, std::forward(tuple)); + } + + template + constexpr void @\exposid{tuple-for-each}@(F&& f, Tuple&& tuple) { // \expos + apply([&](Ts&&... elements) { + (invoke(f, std::forward(elements)), ...); + }, std::forward(tuple)); + } +\end{codeblock} + +\pnum +Given some pack of types \tcode{Ts}, +the alias template \exposid{tuple-or-pair} is defined as follows: +\begin{itemize} +\item +If \tcode{sizeof...(Ts)} is 2, +\tcode{\exposid{tuple-or-pair}} denotes \tcode{pair}. +\item +Otherwise, \tcode{\exposid{tuple-or-pair}} denotes \tcode{tuple}. +\end{itemize} + \rSec2[range.all]{All view} \rSec3[range.all.general]{General} @@ -8761,22 +8803,6 @@ (!(@\libconcept{bidirectional_range}@ && ...) && (@\libconcept{common_range}@ && ...)) || ((@\libconcept{random_access_range}@ && ...) && (@\libconcept{sized_range}@ && ...)); - template - constexpr auto @\exposid{tuple-transform}@(F&& f, Tuple&& tuple) { // \expos - return apply([&](Ts&&... elements) { - return tuple...>( - invoke(f, std::forward(elements))... - ); - }, std::forward(tuple)); - } - - template - constexpr void @\exposid{tuple-for-each}@(F&& f, Tuple&& tuple) { // \expos - apply([&](Ts&&... elements) { - (invoke(f, std::forward(elements)), ...); - }, std::forward(tuple)); - } - template<@\libconcept{input_range}@... Views> requires (@\libconcept{view}@ && ...) && (sizeof...(Views) > 0) class zip_view : public view_interface> { @@ -13603,3 +13629,762 @@ Equivalent to: \tcode{ranges::iter_swap(x.\exposid{current_}, y.\exposid{current_});} \end{itemdescr} + +\rSec2[range.cartesian]{Cartesian product view} + +\rSec3[range.cartesian.overview]{Overview} + +\indexlibraryglobal{cartesian_product_view}% +\pnum +\tcode{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. + +\pnum +The name \tcode{views::cartesian_product} denotes a customization point object\iref{customization.point.object}. +Given a pack of subexpressions \tcode{Es}, +the expression \tcode{views::cartesian_product(Es...)} +is expression-equivalent to +\begin{itemize} +\item +\tcode{\exposid{decay-copy}(views::empty>)} +if \tcode{Es} is an empty pack, +\item +otherwise, +\tcode{cartesian_product_view...>(Es...)}. +\end{itemize} + +\pnum +\begin{example} +\begin{codeblock} +std::vector v { 0, 1, 2 }; +for (auto&& [a,b,c] : std::views::cartesian_product(v, v, v)) { + std::cout << a << ' ' << b << ' ' << c << '\n'; + // 0 0 0 + // 0 0 1 + // 0 0 2 + // 0 1 0 + // 0 1 1 + // ... +} +\end{codeblock} +\end{example} + +\rSec3[range.cartesian.view]{Class template \tcode{cartesian_product_view}} + +\begin{codeblock} +namespace std::ranges { + template + concept @\defexposconcept{cartesian-product-is-random-access}@ = // \expos + (@\libconcept{random_access_range}@<@\exposid{maybe-const}@> && ... && + (@\libconcept{random_access_range}@<@\exposid{maybe-const}@> + && @\libconcept{sized_range}@<@\exposid{maybe-const}@>)); + + template + concept @\defexposconcept{cartesian-product-common-arg}@ = // \expos + @\libconcept{common_range}@ || (@\libconcept{sized_range}@ && @\libconcept{random_access_range}@); + + template + concept @\defexposconcept{cartesian-product-is-bidirectional}@ = // \expos + (@\libconcept{bidirectional_range}@<@\exposid{maybe-const}@> && ... && + (@\libconcept{bidirectional_range}@<@\exposid{maybe-const}@> + && @\exposconcept{cartesian-product-common-arg}@<@\exposid{maybe-const}@>)); + + template + concept @\defexposconcept{cartesian-product-is-common}@ = // \expos + @\exposconcept{cartesian-product-common-arg}@>; + + template + concept @\defexposconcept{cartesian-product-is-sized}@ = // \expos + (@\libconcept{sized_range}@ && ...); + + template class FirstSent, class First, class... Vs> + concept @\defexposconcept{cartesian-is-sized-sentinel}@ = // \expos + (@\libconcept{sized_sentinel_for}@, + iterator_t<@\exposid{maybe-const}@> && ... + && (@\libconcept{sized_range}@<@\exposid{maybe-const}@> + && @\libconcept{sized_sentinel_for}@>, + iterator_t<@\exposid{maybe-const}@>>)); + + template<@\exposconcept{cartesian-product-common-arg}@ R> + constexpr auto @\exposid{cartesian-common-arg-end}@(R& r) { // \expos + if constexpr (@\libconcept{common_range}@) { + return ranges::end(r); + } else { + return ranges::begin(r) + ranges::distance(r); + } + } + + template<@\libconcept{input_range}@ First, @\libconcept{forward_range}@... Vs> + requires (@\libconcept{view}@ && ... && @\libconcept{view}@) + class cartesian_product_view : public view_interface> { + private: + tuple @\exposid{bases_}@; // \expos + // \ref{ranges.cartesian.iterator}, class template \tcode{cartesian_product_view::\exposid{iterator}} + template class @\exposid{iterator}@; // \expos + public: + constexpr cartesian_product_view() = default; + constexpr explicit cartesian_product_view(First first_base, Vs... bases); + + constexpr @\exposid{iterator}@ begin() + requires (!@\exposconcept{simple-view}@ || ... || !@\exposconcept{simple-view}@); + constexpr @\exposid{iterator}@ begin() const + requires (@\libconcept{range}@ && ... && @\libconcept{range}@); + + constexpr @\exposid{iterator}@ end() + requires ((!@\exposconcept{simple-view}@ || ... || !@\exposconcept{simple-view}@) && + @\exposconcept{cartesian-product-is-common}@); + constexpr @\exposid{iterator}@ end() const + requires @\exposconcept{cartesian-product-is-common}@; + constexpr default_sentinel_t end() const noexcept; + + constexpr @\seebelow@ size() + requires @\exposconcept{cartesian-product-is-sized}@; + constexpr @\seebelow@ size() const + requires @\exposconcept{cartesian-product-is-sized}@; + }; + + template + cartesian_product_view(Vs&&...) -> cartesian_product_view...>; +} +\end{codeblock} + +\begin{itemdecl} +constexpr explicit cartesian_product_view(First first_base, Vs... bases); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{bases_} +with \tcode{std::move(first_base), std::move(bases)...}. +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@ begin() + requires (!@\exposconcept{simple-view}@ || ... || !@\exposconcept{simple-view}@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\tcode{return \exposid{iterator}(\exposid{tuple-transform}(ranges::begin, \exposid{bases_}));} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@ begin() const + requires (@\libconcept{range}@ && ... && @\libconcept{range}@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\tcode{return \exposid{iterator}(\exposid{tuple-transform}(ranges::begin, \exposid{bases_}));} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@ end() + requires ((!@\exposconcept{simple-view}@ || ... || !@\exposconcept{simple-view}@) + && @\exposconcept{cartesian-product-is-common}@); +constexpr @\exposid{iterator}@ end() const + requires @\exposconcept{cartesian-product-is-common}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let: +\begin{itemize} +\item +\exposid{is-const} be \tcode{true} for the const-qualified overload, and +\tcode{false} otherwise; +\item +\exposid{is-empty} be \tcode{true} +if the expression \tcode{ranges::empty(rng)} is \tcode{true} +for any \tcode{rng} among the underlying ranges except the first one and +\tcode{false} otherwise; and +\item +\tcode{\exposid{begin-or-first-end}(rng)} be expression-equivalent to +\tcode{\exposid{is-empty} ? ranges::begin(rng) : \exposid{cartesian-common-arg-end}(rng)} +if \tcode{rng} is the first underlying range and +\tcode{ranges::begin(rng)} otherwise. +\end{itemize} + +\pnum +\effects +Equivalent to: +\begin{codeblock} +@\exposid{iterator}@ it(@\exposid{tuple-transform}@( + [](auto& rng){ return @\exposid{begin-or-first-end}@(rng); }, @\exposid{bases_}@)); +return it; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr default_sentinel_t end() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{default_sentinel}. +\end{itemdescr} + +\begin{itemdecl} +constexpr @\seebelow@ size() + requires @\exposconcept{cartesian-product-is-sized}@; +constexpr @\seebelow@ size() const + requires @\exposconcept{cartesian-product-is-sized}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The return type is an \impldef{return type of \tcode{cartesian_product_view::size}} unsigned-integer-like type. + +\pnum +\recommended +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. + +\pnum +Let $p$ be the product of the sizes of all the ranges in \tcode{bases_}. + +\pnum +\expects +$p$ can be represented by the return type. + +\pnum +\returns +$p$. +\end{itemdescr} + +\rSec3[ranges.cartesian.iterator]{Class template \tcode{cartesian_product_view::\exposid{iterator}}} + +\begin{codeblock} +namespace std::ranges { + template<@\libconcept{input_range}@ First, @\libconcept{forward_range}@... Vs> + requires (@\libconcept{view}@ && ... && @\libconcept{view}@) + template + class cartesian_product_view::@\exposid{iterator}@ { + public: + using iterator_category = input_iterator_tag; + using iterator_concept = @\seebelow@; + using value_type = @\exposid{tuple-or-pair}@>, + range_value_t<@\exposid{maybe-const}@>...>; + using reference = @\exposid{tuple-or-pair}@>, + reference_t<@\exposid{maybe-const}@>...>; + using difference_type = @\seebelow@; + + @\exposid{iterator}@() requires @\libconcept{forward_range}@<@\exposid{maybe-const}@> = default; + + constexpr @\exposid{iterator}@(@\exposid{iterator}@ i) requires Const && + (@\libconcept{convertible_to}@, iterator_t<@\exposid{maybe-const}@>> && + ... && @\libconcept{convertible_to}@, iterator_t<@\exposid{maybe-const}@>>); + + constexpr auto operator*() const; + constexpr @\exposid{iterator}@& operator++(); + constexpr void operator++(int); + constexpr @\exposid{iterator}@ operator++(int) requires @\libconcept{forward_range}@<@\exposid{maybe-const}@>; + + constexpr @\exposid{iterator}@& operator--() + requires @\exposconcept{cartesian-product-is-bidirectional}@; + constexpr @\exposid{iterator}@ operator--(int) + requires @\exposconcept{cartesian-product-is-bidirectional}@; + + constexpr @\exposid{iterator}@& operator+=(difference_type x) + requires @\exposconcept{cartesian-product-is-random-access}@; + constexpr @\exposid{iterator}@& operator-=(difference_type x) + requires @\exposconcept{cartesian-product-is-random-access}@; + + constexpr reference operator[](difference_type n) const + requires @\exposconcept{cartesian-product-is-random-access}@; + + friend constexpr bool operator==(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires @\libconcept{equality_comparable}@>>; + + friend constexpr bool operator==(const @\exposid{iterator}@& x, default_sentinel_t); + + friend constexpr auto operator<=>(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires @\exposconcept{all-random-access}@; + + friend constexpr @\exposid{iterator}@ operator+(const @\exposid{iterator}@& x, difference_type y) + requires @\exposconcept{cartesian-product-is-random-access}@; + friend constexpr @\exposid{iterator}@ operator+(difference_type x, const @\exposid{iterator}@& y) + requires @\exposconcept{cartesian-product-is-random-access}@; + friend constexpr @\exposid{iterator}@ operator-(const @\exposid{iterator}@& x, difference_type y) + requires @\exposconcept{cartesian-product-is-random-access}@; + friend constexpr difference_type operator-(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires @\exposconcept{cartesian-is-sized-sentinel}@; + + friend constexpr difference_type operator-(@\exposid{iterator}@ i, default_sentinel_t) + requires @\exposconcept{cartesian-is-sized-sentinel}@; + friend constexpr difference_type operator-(default_sentinel_t, @\exposid{iterator}@ i) + requires @\exposconcept{cartesian-is-sized-sentinel}@; + + friend constexpr auto iter_move(const @\exposid{iterator}@& i) noexcept(@\seebelow@); + + friend constexpr void iter_swap(const @\exposid{iterator}@& l, const @\exposid{iterator}@& r) noexcept(@\seebelow@) + requires (@\libconcept{indirectly_swappable}@>> && ... && + @\libconcept{indirectly_swappable}@>>); + + private: + @\exposid{maybe-const}@* @\exposid{parent_}@ = nullptr; // \expos + @\exposid{tuple-or-pair}@>, + iterator_t<@\exposid{maybe-const}@>...> @\exposid{current_}@; // \expos + + template + constexpr void @\exposid{next}@(); // \expos + + template + constexpr void @\exposid{prev}@(); // \expos + + template + constexpr difference_type @\exposid{distance-from}@(Tuple t); // \expos + + constexpr explicit @\exposid{iterator}@(@\exposid{tuple-or-pair}@>, + iterator_t<@\exposid{maybe-const}@>...> current); // \expos + }; +} +\end{codeblock} + +\pnum +\tcode{\exposid{iterator}::iterator_concept} is defined as follows: +\begin{itemize} +\item +If \tcode{\exposconcept{cartesian-product-is-random-access}} +is modeled, +then \tcode{iterator_con\-cept} denotes \tcode{random_access_iterator_tag}. +\item +Otherwise, +if \tcode{\exposconcept{cartesian-product-is-bidirectional}} +is modeled, +then \tcode{it\-erator_concept} denotes \tcode{bidirectional_iterator_tag}. +\item +Otherwise, +if \tcode{\exposid{maybe-const}} models \libconcept{forward_range}, +then \tcode{iterator_concept} denotes \tcode{forward_iterator_tag}. +\item +Otherwise, \tcode{iterator_concept} denotes \tcode{input_iterator_tag}. +\end{itemize} + +\pnum +\tcode{\exposid{iterator}::difference_type} is +an \impldef{type of \tcode{ranges::cartesian_product_view::\exposid{iterator}::difference_type}} +signed-integer-like type. + +\pnum +\recommended +\tcode{\exposid{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. + +\pnum +\begin{itemdecl} +template + constexpr void @\exposid{next}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto& it = std::get(@\exposid{current_}@); +++it; +if constexpr (N > 0) { + if (it == ranges::end(std::get(@\exposid{parent_}@->@\exposid{bases_}@))) { + it = ranges::begin(std::get(@\exposid{parent_}@->@\exposid{bases_}@)); + @\exposid{next}@(); + } +} +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +template + constexpr void @\exposid{prev}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto& it = std::get(@\exposid{current_}@); +if (it == ranges::begin(std::get(@\exposid{parent_}@->@\exposid{bases_}@))) { + it = @\exposid{cartesian-common-arg-end}@(std::get(@\exposid{parent_}@->@\exposid{bases_}@)); + if constexpr (N > 0) { + @\exposid{prev}@(); + } +} +--it; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +template + constexpr difference_type @\exposid{distance-from}@(Tuple t); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let: +\begin{itemize} +\item +$\exposid{scaled-size}(N)$ be the product of +\tcode{static_cast(ranges::size(std::get<\brk{}$N$>(\exposid{parent_}->\exposid{bases_})))} and +$\exposid{scaled-size}(N+1)$ +if $N < \tcode{sizeof...(Vs)}$, otherwise \tcode{static_cast(1)}; +\item +$\exposid{scaled-distance}(N)$ be the product of +\tcode{static_cast(std::get<$N$>(\exposid{cur\-rent_}) - std::get<$N$>(t))} and $\exposid{scaled-size}(N+1)$; and +\item +\exposid{scaled-sum} be the sum of $\exposid{scaled-distance}(N)$ +for every integer $0 \le N \le \tcode{sizeof...(Vs)}$. +\end{itemize} + +\pnum +\expects +\exposid{scaled-sum} can be represented by \tcode{difference_type}. + +\pnum +\returns +\exposid{scaled-sum}. +\end{itemdescr} + +\begin{itemdecl} +constexpr explicit @\exposid{iterator}@(@\exposid{tuple-or-pair}@>, + iterator_t>...> current); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{current_} with \tcode{std::move(current)}. +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@(@\exposid{iterator}@ i) requires Const && + (@\libconcept{convertible_to}@, iterator_t<@\exposid{maybe-const}@>> && + ... && @\libconcept{convertible_to}@, iterator_t<@\exposid{maybe-const}@>>); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{current_} with \tcode{std::move(i.\exposid{current_})}. +\end{itemdescr} + +\begin{itemdecl} +constexpr auto operator*() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +return @\exposid{tuple-transform}@([](auto& i) -> decltype(auto) { return *i; }, @\exposid{current_}@); +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@& operator++(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +@\exposid{next}@(); +return *this; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr void operator++(int); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{++*this}. +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@ operator++(int) requires @\libconcept{forward_range}@<@\exposid{maybe-const}@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto tmp = *this; +++*this; +return tmp; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@& operator--() + requires @\exposconcept{cartesian-product-is-bidirectional}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +@\exposid{prev}@(); +return *this; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@ operator--(int) + requires @\exposconcept{cartesian-product-is-bidirectional}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto tmp = *this; +--*this; +return tmp; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@& operator+=(difference_type x) + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{orig} be the value of \tcode{*this} before the call. + +Let \tcode{ret} be: +\begin{itemize} +\item +If \tcode{x > 0}, +the value of \tcode{*this} had \exposid{next} been called \tcode{x} times. +\item +Otherwise, if \tcode{x < 0}, +the value of \tcode{*this} had \exposid{prev} been called \tcode{-x} times. +\item +Otherwise, \tcode{orig}. +\end{itemize} + +\pnum +\expects +\tcode{x} is in the range +$[\tcode{ranges::distance(*this, ranges::begin(*\exposid{parent_}))},$\newline +$\tcode{ranges::distance(*this, ranges::end(*\exposid{parent_}))}]$. + +\pnum +\effects +Sets the value of \tcode{*this} to \tcode{ret}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\complexity +Constant. +\end{itemdescr} + +\begin{itemdecl} +constexpr @\exposid{iterator}@& operator-=(difference_type x) + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +*this += -x; +return *this; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr reference operator[](difference_type n) const + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return *((*this) + n);} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr bool operator==(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires \libconcept{equality_comparable}>>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return x.\exposid{current_} == y.\exposid{current_};} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr bool operator==(const @\exposid{iterator}@& x, default_sentinel_t); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{true} if \tcode{std::get<$i$>(x.\exposid{current_}) == ranges::end(std::get<$i$>(x.\exposid{parent_}->\exposid{bases_}))} +is \tcode{true} +for any integer $0 \le i \le \tcode{sizeof...(Vs)}$; +otherwise, \tcode{false}. +\end{itemdescr} + +\begin{itemdecl} +friend constexpr auto operator<=>(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires @\exposconcept{all-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return x.\exposid{current_} <=> y.\exposid{current_};} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr @\exposid{iterator}@ operator+(const @\exposid{iterator}@& x, difference_type y) + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{iterator}(x) += y;} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr @\exposid{iterator}@ operator+(difference_type x, const @\exposid{iterator}@& y) + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return y + x;} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr @\exposid{iterator}@ operator-(const @\exposid{iterator}@& x, difference_type y) + requires @\exposconcept{cartesian-product-is-random-access}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{iterator}(x) -= y;} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr difference_type operator-(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + requires @\exposconcept{cartesian-is-sized-sentinel}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return x.\exposid{distance-from}(y.\exposid{current_});} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr difference_type operator-(@\exposid{iterator}@ i, default_sentinel_t) + requires @\exposconcept{cartesian-is-sized-sentinel}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \exposid{end-tuple} be an object of a type +that is a specialization of \tcode{tuple}, such that: +\begin{itemize} +\item +\tcode{std::get<0>(\exposid{end-tuple})} has the same value as +\tcode{ranges::end(std::get<0>(i.\exposid{parent_}->\exposid{ba\-ses_}))}; +\item +\tcode{std::get<$N$>(\exposid{end-tuple})} has the same value as +\tcode{ranges::begin(std::get<$N$>(i.\exposid{parent_}->\exposid{bases_}))} +for every integer $1 \le N \le \tcode{sizeof...(Vs)}$. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{return i.\exposid{distance-from}(\exposid{end-tuple});} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr difference_type operator-(default_sentinel_t s, @\exposid{iterator}@ i) + requires @\exposconcept{cartesian-is-sized-sentinel}@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return -(i - s);} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr auto iter_move(const @\exposid{iterator}@& i) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{tuple-transform}(ranges::iter_move, i.\exposid{current_});} + +\pnum +\remarks +The exception specification is equivalent to +the logical AND of the following expressions: +\begin{itemize} +\item +\tcode{noexcept(ranges::iter_move(std::get<$N$>(i.\exposid{current_}))} +for every integer\newline $0 \le N \le \tcode{sizeof...(Vs)}$, +\item +\tcode{is_nothrow_move_constructible_v>>}\newline +for every type \tcode{T} in \tcode{F, Vs...}. +\end{itemize} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr void iter_swap(const @\exposid{iterator}@& l, const @\exposid{iterator}@& r) noexcept(@\seebelow@) + requires (@\libconcept{indirectly_swappable}@>> && ... && + @\libconcept{indirectly_swappable}@>>); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +For every integer $0 \le i \le \tcode{sizeof...(Vs)}$, performs: +\begin{codeblock} +ranges::iter_swap(std::get<@$i$@>(l.@\exposid{current_}@), std::get<@$i$@>(r.@\exposid{current_}@)) +\end{codeblock} + +\pnum +\remarks +The exception specification is equivalent to the logical AND of the following expressions: +\begin{itemize} +\item +\tcode{noexcept(ranges::iter_swap(std::get<$i$>(l.\exposid{current_}), std::get<$i$>(r.\exposid{current_})))} +for\newline every integer $0 \le i \le \tcode{sizeof...(Vs)}$. +\end{itemize} +\end{itemdescr} diff --git a/source/support.tex b/source/support.tex index d0c1397c32..0cd26e7b0b 100644 --- a/source/support.tex +++ b/source/support.tex @@ -678,6 +678,7 @@ #define @\defnlibxname{cpp_lib_ranges}@ 202202L // also in \libheader{algorithm}, \libheader{functional}, \libheader{iterator}, \libheader{memory}, \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_as_const}@ 202207L // also in \libheader{ranges} +#define @\defnlibxname{cpp_lib_ranges_cartesian_product}@ 202207L // also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_chunk}@ 202202L // also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_chunk_by}@ 202202L // also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_contains}@ 202207L // also in \libheader{algorithm} From d899065cc9cb44e87919803657f55fe7a57110e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Wed, 17 Aug 2022 00:34:49 +0100 Subject: [PATCH 2/2] [range.cartesian] Replace "tuple-or-pair" with "tuple". These changes are part of LWG-Motion 12 (P2165R4, "Compatibility between tuple and tuple-like objects"). --- source/ranges.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/ranges.tex b/source/ranges.tex index e5791ca17f..3731b8a042 100644 --- a/source/ranges.tex +++ b/source/ranges.tex @@ -13871,9 +13871,9 @@ public: using iterator_category = input_iterator_tag; using iterator_concept = @\seebelow@; - using value_type = @\exposid{tuple-or-pair}@>, + using value_type = tuple>, range_value_t<@\exposid{maybe-const}@>...>; - using reference = @\exposid{tuple-or-pair}@>, + using reference = tuple>, reference_t<@\exposid{maybe-const}@>...>; using difference_type = @\seebelow@; @@ -13931,7 +13931,7 @@ private: @\exposid{maybe-const}@* @\exposid{parent_}@ = nullptr; // \expos - @\exposid{tuple-or-pair}@>, + tuple>, iterator_t<@\exposid{maybe-const}@>...> @\exposid{current_}@; // \expos template @@ -13943,7 +13943,7 @@ template constexpr difference_type @\exposid{distance-from}@(Tuple t); // \expos - constexpr explicit @\exposid{iterator}@(@\exposid{tuple-or-pair}@>, + constexpr explicit @\exposid{iterator}@(tuple>, iterator_t<@\exposid{maybe-const}@>...> current); // \expos }; } @@ -14057,7 +14057,7 @@ \end{itemdescr} \begin{itemdecl} -constexpr explicit @\exposid{iterator}@(@\exposid{tuple-or-pair}@>, +constexpr explicit @\exposid{iterator}@(tuple>, iterator_t>...> current); \end{itemdecl}