1. Changelog
1.1. Changes from r3
LWG review comments:
- 
     Fixed various typos and applied minor comments. 
- 
     Fix fold expressions to be uniform between the forward declaration and the definition; add parentheses to fold expressions where previously missed. 
- 
     Removed constraints from the default_sentinel_t end noexcept 
- 
     Reworded the specification of size 
- 
     Reformatted the document to correctly italicize exposition only names and provide paragraph numbers in the specification. 
LWG review comments, second round:
- 
     The non-sentinel overloads of end begin 
- 
     Add a void version of operator ++ ( int ) 
- 
     A lot of the maybe - const cartesian - product - common - arg cartesian - product - is - common cartesian - product - is - sized 
- 
     Applied an equivalent of the resolution of LWG3692 to this paper, removing the relational operators in iterator and removing spurious constraints from spaceship. 
- 
     cartesian - sentinel - is - sized cartesian - is - sized - sentinel maybe - const iterator_t sentinel_t 
- 
     The definition of distance - to scaled_distance 
- 
     The definition of operator += prev next 
- 
     Various small fixes to make sure constraints, semantics, and formatting are correct. 
LWG review comments, mailing list review:
- 
     Fixed the handling of a cartesian product with an empty non-first range in comparison with default_sentinel_t 
- 
     The new description of cartesian_product_view views :: cartesian_product 
- 
     Various typo fixes. 
LWG review comments, third round:
- 
     Rename distance - to distance - from std :: distance 
- 
     Add a missing "Returns: *this." to operator += 
- 
     Rephrase the precondition of operator += ranges :: distance 
- 
     Replace "for every N in the interval [x, y]" with "for every integer x ≤ N ≤ y" throughout the paper. 
- 
     Reword the exception specification of iter_move iter_swap 
- 
     Various typo and formatting fixes. 
1.2. Changes from r2
- 
     Make the size and difference types implementation-defined and only recommend requirements for them. 
1.3. Changes from r1
- 
     Add wording adding cartesian_product_view < ranges > 
- 
     Made the constructor of the view explicit 
- 
     Added a design section about not making cartesian_product_view 
- 
     Added a feature test macro. 
- 
     Use the verbiage about the return type of size difference_type 
- 
     Relax the requirements on the first range, allowing it to be an input range. 
- 
     Relax the requirements on the ranges for the view to be a common range - it now only depends on the properties of the first range. 
- 
     Add operator - 
- 
     Various wording fixes. 
1.4. Changes from r0
- 
     Specify the return type of size 
- 
     Add design section on the first range argument. 
2. Motivation
Cartesian product is a fundamental mathematical construct. There should be a 
| Before | After | 
|---|---|
| 
 | 
 | 
This is especially useful for composing with other views, or dealing with parameter packs of ranges:
| Before | After | 
|---|---|
| 
 | 
 | 
3. Design
3.1. Minimum Range Requirements
This paper requires all ranges, except the first one, passed tocartesian_product_view 3.2. Tuple or pair
A potential design would be to usetuple value_type reference_type cartesian_product_view std :: pair tuple 3.3. reference_type 
    This paper uses tuple - or - pair < ranges :: reference_t < Vs > ... > pair tuple reference_type 3.4. Empty cartesian product view
Trying to take the cartesian product view of 0 views will produce anempty_view < tuple <>> 3.5. Common Range
cartesian_product_view 3.6. Bidirectional Range
cartesian_product_view We don’t consider non-common, random access, sized ranges as worth supporting, so this paper requires bidirectional and common.
3.7. Random Access Range
cartesian_product_view We can’t think of too many use-cases for this and it adds a fair bit of implementation burden, but this paper supports the necessary operations.
3.8. Initial Range Argument Special-Casing
The first range passed tocartesian_product_view - 
     It could be an input range instead of forward range 
- 
     It wouldn’t need to be a sized range in order for the cartesian_product_view 
- 
     It wouldn’t need to be common in order for the cartesian_product_view 
Previous revisions of this paper didn’t propose this feature, however the Ranges SG has requested that it be added to the paper.
3.9. Sized Range
cartesian_product_view 3.10. Naming
An alternative name isstd :: ranges :: product_view cartesian_product_view 3.11. Pipe Support
It may be possible to support syntax such asvec1  |  views :: cartesian_product ( vec2 ) cartesian_product_view However, it’s problematic for the same reason as 
3.12. Borrowed Range?
It is possible to implementcartesian_product_view cartesian_product_view Due to this, this paper does not make 
4. Implementation
There are implementations of a cartesian product view in Range-v3, cor3ntin::rangesnext, and tl::ranges, among others.5. Wording
5.1. Addition to < ranges > 
   Add the following to 24.2 [ranges.syn], Header 
// ... namespace std :: ranges { // ... // [range.cartesian], cartesian product view template < input_range First , forward_range ... Vs > requires ( view < First > && ... && view < Vs > ) class cartesian_product_view ; namespace views { inline constexpr unspecified cartesian_product = unspecified ; } } 
5.2. Range adaptor helpers [range.adaptor.helpers]
Add a new section after Non-propagating cache [range.nonprop.cache]. Remove the definitions of 
namespace std :: ranges { 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 )); } } 
Given some pack of types Ts, the alias template tuple-or-pair is defined as follows:
- 
     If sizeof ...( Ts ) tuple - or - pair < Ts ... > pair < Ts ... > 
- 
     Otherwise, tuple - or - pair < Ts ... > tuple < Ts ... > 
5.3. Cartesian product view [range.cartesian]
5.3.1. Overview [range.cartesian.overview]
- 
     cartesian_product_view 
- 
     The name views :: cartesian_product Es ... views :: cartesian_product ( Es ...) 
- 
     decay - copy ( views :: empty < tuple <>> ) Es 
- 
     otherwise, cartesian_product_view < views :: all_t < decltype (( Es )) > ... > ( Es ...) 
[Example:
-- end example ]std :: vector < int > 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 //... } 
5.3.2. Class template cartesian_product_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 < Const , First >> ; template < class ... Vs > concept cartesian - product - is - sized = // exposition only ( sized_range < Vs > && ...); template < bool Const , template < class > class FirstSent , typename First , typename ... 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 ) { 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 template < bool Const > struct 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 > ... > ; namespace views { inline constexpr unspecified cartesian_product = unspecified ; } } 
constexpr explicit cartesian_product_view ( First first_base , Vs ... bases ); 
- 
     Effects: Initialises bases_ 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 truefor the const-qualified overload, andfalseotherwise;
- 
     is - empty trueif the expressionranges :: empty ( rng ) truefor anyrng falseotherwise; and
- 
     begin - or - first - end ( rng ) is - empty ? ranges :: begin ( rng ) : cartesian - common - arg - end ( rng ) rng ranges :: begin ( rng ) 
- 
     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. 
5.3.3. Class template cartesian_product_view :: 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 - or - pair < range_value_t < maybe - const < Const , First >> , range_value_t < maybe - const < Const , Vs >> ... > ; using reference = tuple - or - pair < reference_t < maybe - const < Const , First >> , 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 < maybe - const < Const , First >>> && ... && convertible_to < iterator_t < Vs > , iterator_t < maybe - const < 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 - or - pair < 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 - or - pair < iterator_t < maybe - const < Const , First >> , iterator_t < maybe - const < Const , Vs >> ... > current ); // exposition only }; } 
- 
     iterator :: iterator_concept 
- 
     If cartesian - product - is - random - access < Const , First , Vs ... > iterator_concept random_access_iterator_tag 
- 
     Otherwise, if cartesian - product - is - bidirectional < Const , First , Vs ... > iterator_concept bidirectional_iterator_tag 
- 
     Otherwise, if maybe - const < Const , First > forward_range iterator_concept forward_iterator_tag 
- 
     Otherwise, iterator_concept input_iterator_tag 
- 
     iterator :: difference_type 
- 
     Recommended practice: iterator :: difference_type 
template < size_t N = sizeof ...( Vs ) > constexpr void next (); // exposition only 
- 
     Effects: Equivalent to: 
auto & it = std :: get < N > ( current_ ); ++ it ; if constexpr ( N > 0 ) { if ( it == ranges :: end ( get < N > ( parent_ -> bases_ ))) { it = ranges :: begin ( std :: get < N > ( parent_ -> bases_ )); next < N - 1 > (); } } 
template < size_t N = sizeof ...( Vs ) > constexpr void prev (); // exposition only 
- 
     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 ); // exposition only 
- 
     Let: 
- 
     scaled_size(N) be the product of static_cast < difference_type > ( ranges :: size ( std :: get < N > ( parent_ -> bases_ ))) sizeof ...( Vs ) static_cast < difference_type > ( 1 ) 
- 
     scaled_distance(N) be the product of static_cast < difference_type > ( std :: get < N > ( current_ ) - std :: get < N > ( t )) 
- 
     scaled_sum be the sum of scaled_distance(N) for every integer 0 ≤ N ≤ sizeof ...( Vs ) 
- 
     Preconditions: scaled_sum can be represented by difference_type 
- 
     Returns: scaled_sum. 
constexpr explicit iterator ( tuple - or - pair < iterator_t < maybe - const < Const , First >> , iterator_t < maybe - const < Const , Vs >> ... > current ); 
- 
     Effects: Initializes current_ std :: move ( current ) 
constexpr iterator ( iterator <! Const > i ) requires Const && ( convertible_to < iterator_t < First > , iterator_t < maybe - const < Const , First >>> && ... && convertible_to < iterator_t < Vs > , iterator_t < maybe - const < Const , Vs >>> ); 
- 
     Effects: Initializes current_ 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 * this 
- 
     Let ret 
- 
     If x > 0 * this next x 
- 
     Otherwise, if x < 0 * this prev x 
- 
     Otherwise, orig 
- 
     Precondition: x ranges :: distance ( * this , ranges :: begin ( * parent_ )) ranges :: distance ( * this , ranges :: end ( * parent_ )) 
- 
     Effects: Sets the value of * this 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: trueifstd :: get < i > ( x . current_ ) == ranges :: end ( std :: get < i > ( x . parent_ -> bases_ )) truefor any integer 0 ≤ i ≤sizeof ...( Vs ) 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 tuple 
- 
     std :: get < 0 > ( end - tuple ) ranges :: end ( std :: get < 0 > ( i . parent_ -> bases_ )) 
- 
     std :: get < N > ( end - tuple ) ranges :: begin ( std :: get < N > ( i . parent_ -> bases_ )) sizeof ...( Vs ) 
- 
     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_ )) sizeof ...( Vs ) 
- 
     is_nothrow_move_constructible_v < range_rvalue_reference_t < maybe - const < Const , T >>> T F , 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 0 ≤ i ≤ sizeof ...( Vs ) 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_ ))) sizeof ...( Vs ) 
5.4. Feature-test macro
Add the following macro definition to 17.3.2 [version.syn], Header 
#define __cpp_lib_ranges_cartesian_product 20XXXXL // also in <ranges> 
6. Acknowledgements
Thank you to Christopher Di Bella, Corentin Jabot, Tim Song, and Barry Revzin for feedback and guidance.
Thank you to Tomasz Kamiński for help with getting the wording into a good shape.