1. Changes
1.1. San Diego Polls
The following polls were taken by LEWG during the San Diego meeting.
| Abbreviation | SF | F | N | A | SA | 
|---|---|---|---|---|---|
| Term | Strongly Favour | Favour | Neutral | Against | Strongly Against | 
We want to prioritize
for C++20 in Kona.basic_istream_view 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 0 | 2 | 6 | 0 | 0 | 
We want to prioritize addition of
,range_difference_t ,range_value_t ,range_reference_t for C++20 in Kona.range_rvalue_reference_t 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 0 | 2 | 6 | 0 | 0 | 
We want to prioritize
for C++20 in Kona.take_while_view 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 2 | 9 | 2 | 0 | 0 | 
We want to prioritize
for C++20 in Kona.drop_view 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 3 | 5 | 5 | 0 | 0 | 
We want to prioritize
for C++20 in Kona.drop_while_view 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 2 | 6 | 5 | 0 | 0 | 
We prefer types for
andkeys_view (in addition).values_view 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 0 | 10 | 3 | 0 | 0 | 
We want to prioritize
andkeys () (and their type forms) for C++20 in Kona.values () 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 5 | 9 | 0 | 0 | 0 | 
Approval
| Result | Poll | 
|---|---|
| 9 | Add conversions ( -qualified assignments) to/ | 
| 6 | Use a hidden implementation detail type | 
| 5 | Ask EWG to fix proxy references ( doesn’t happen for C++23 or worse) | 
| 0 | Do not want  | 
| 14 | Ask EWG to fix proxy references, and decouple from  | 
Interest in
(where the value type iszip_longest () ortuple < optional < T > ... > with some value)tuple < T ... > 
| SF | F | N | A | SA | 
|---|---|---|---|---|
| 2 | 3 | 5 | 3 | 0 | 
1.2. From R3 to R4
- 
     Proposes that iterator_t sentinel_t Range 
- 
     Adjusts associated types for ranges so that they don’t explicitly require Range iterator_t 
- 
     Adjusts keys_view values_view 
1.3. From R2 to R3
- 
     Adds polls from San Diego meeting. 
- 
     Removed range_size_t range_common_iterator_t 
- 
     Added justification for why is_object_v take_while_view 
- 
     Replaced contract-specified pre-conditions with text-specified pre-conditions. 
- 
     Removed concept StreamInsertable 
- 
     Replaced concept StreamExtractable stream - extractable - 
       This was done, in part, to balance the fact that a concept would exist for operator >> operator << 
 
- 
       
- 
     Replaced pros and cons of __tuple_hack const std :: tuple common_type basic_common_type 
1.3.1. Changes promised for R4
- 
     Apply additions to std :: tuple std :: pair 
- 
     Considering zip_longest 
- 
     Changes to keys values 
1.4. From R1 to R2
- 
     Expanded acknowledgements and co-authors. 
- 
     Removed zip_with_view 
- 
     Added zip_view 
- 
     Added keys values 
- 
     Added content for associated types for ranges. 
1.5. From R0 to R1
- 
     Revised istream_range 
- 
     Renamed to basic_istream_view 
- 
     Introduced some relevant concepts. 
- 
     Introduced drop_view take_while_view drop_while_view 
- 
     Teased zip_with_view 
- 
     Teased associated types for ranges. 
2. Acknowledgements
We would like to acknowledge the following people for their assistance with this proposal:
- 
     Eric Niebler, for providing [range-v3] as a reference implementation. 
- 
     Tim Song, Steve Downey, and Barry Revzin for their reviews of P1035. 
3. Motivation
[P0789] introduces the notion of a range adaptor and twelve pioneering range adaptors that improve declarative, range-based programming. For example, it is possible to perform an inline, in-place, lazy reverse like so:
namespace ranges = std :: ranges ; namespace view = std :: ranges :: view ; // Old auto i = ranges :: find ( ranges :: rbegin ( employees ), ranges :: rend ( employees ), "Lovelace" , & employee :: surname ); // New auto j = ranges :: find ( employees | view :: reverse , "Lovelace" , & employee :: surname ); [[ assert : i == j ]]; 
P1035 recognises that P0789 introduces only a few of the widely experimented-with range adaptors in [range-v3], and would like to add a few more to complete the C++20 phase of range adaptors.
4. Proposals
Unless otherwise requested, each sub-subsection below should be polled individually from other sub-subsections. Two major questions are to be asked per range adaptor. It is up to LEWG to decide the exact phrasing, but the author’s intentions are listed below.
- 
     Do we want this range adaptor in C++20? - 
       As-is? 
- 
       With modifications, as suggested by LEWG? 
 
- 
       
- 
     If we do not want this range adaptor in C++20, do we want it in C++23? - 
       As-is? 
- 
       With modificaitons, as suggested by LEWG? 
 
- 
       
4.1. zip_view 
   Note: This section was formerly titled 
4.1.1. Motivation
A zip, also known as a [convolution] operation, performs a transformation on multiple input ranges. The typical zip operation transforms several input ranges into a single input range containing a tuple with the ith element from each range, and terminates when the smallest finite range is delimited.
Iterating over multiple ranges simultaneously is a useful feature. Both [EWG43] and [P0026] ask
for this functionality at a language level. While these papers request a useful feature that
benefits raw loops, they don’t address algorithmic composition, which is important for ensuring both
correctness and simplicity. A 
| Current (C++17) | Proposed (C++20) | 
|---|---|
| 
 | 
 | 
Another motivating example for using 
| Current (C++17) | Proposed (C++20) | 
|---|---|
| 
 | 
 | 
Finally, people who work in the parallelism and heterogeneous industries are not able to take full
advantage of the Parallel STL at present due to the limited number of input ranges each algorithm
can support. 
The benefits of this proposed approach include:
- 
     More declared operations, leading to more declarative -- rather than imperative -- styled programming. 
- 
     Eliminates state. 
- 
     A resulting expression can be declared const 
- 
     Temporary storage is eliminated, which helps to improve correctness, clarity, and performance. P0836 §2.1 expands on the performance benefits for heterogeneous programming. 
4.1.1.1. zip_with 
   
vector < float > const x = // ... vector < float > const y = // ... float const a = // ... // ... auto ax = x | view :: transform ([ a ]( auto const x ) noexcept { return a * x ; }); auto saxpy = view :: zip_with ( plus <> {}, ax , y ); auto const result = vector ( begin ( saxpy ), end ( saxpy )); 
As 
template < class F > constexpr auto compose_apply ( F && f ) { return [ f = std :: forward < F > ( f )]( auto && t ) { return std :: apply ( std :: forward < F > ( f ), std :: forward < decltype ( t ) > ( t )); }; }; // ... auto saxpy = zip ( ax , y ) | transform ( compose_apply ( plus <> {})); 
This isn’t an ideal approach, but some of the finer details of 
4.1.2. Problems with pair tuple 
   
Both range-v3 and the cmcstl2 implementation above use an exposition-only type derived from 
Adding a third (and possibly fourth) tuple type that is exposition-only is not ideal: this
requires extensions to both 
Alternatively, we can provide an implicit conversion from 
template < class ... Ts > struct tuple { // ... constexpr tuple const & operator = ( tuple < _Elements ... > const & ) requires ( __stl2 :: Assignable < _Elements const & , _Elements const &> and ...); constexpr tuple const & operator = ( tuple < _Elements ... >&& ) requires ( __stl2 :: Assignable < _Elements const & , _Elements > and ...); template < class ... Us > requires sizeof ...( Ts ) == sizeof ...( Us ) and ( Assignable < Ts const & , Us const &> and ...) constexpr tuple const & operator = ( tuple < Us ... > const & other ) const ; template < class ... Us > requires sizeof ...( Ts ) == sizeof ...( Us ) and ( Assignable < Ts const & , Us const &> and ...) constexpr tuple const & operator = ( tuple < Us ... >&& other ) const ; constexpr operator tuple < remove_reference_t < _Elements >& ... > () noexcept ; constexpr operator tuple < remove_reference_t < _Elements > const & ... > () const noexcept ; constexpr operator tuple < remove_reference_t < _Elements >&& ... > () noexcept ; constexpr operator tuple < remove_reference_t < _Elements > const && ... > () const noexcept ; }; template < class ... Ts > requires ( requires { typename common_type_t < Ts , Ts &> ; } && ...) struct common_type < tuple < Ts & ... > , std :: tuple < Ts ... >> { using type = tuple < common_type_t < Ts & , Ts > ... > ; }; template < class ... Ts > requires ( requires { typename common_type_t < Ts , Ts &> ; } && ...) struct common_type < std :: tuple < Ts ... > , tuple < Ts & ... >> { using type = tuple < common_type_t < Ts & , Ts > ... > ; }; // lvalue to rvalue reference template < class ... Ts > requires ( requires { typename common_type_t < Ts , remove_reference_t < Ts >&&> ; } && ...) struct common_type < tuple < remove_reference_t < Ts >&& ... > , std :: tuple < Ts ... >> { using type = tuple < common_type_t < remove_reference_t < Ts >&& , Ts > ... > ; }; template < class ... Ts > requires ( requires { typename common_type_t < Ts , remove_reference_t < Ts >&&> ; } && ...) struct common_type < std :: tuple < Ts ... > , tuple < remove_reference_t < Ts >&& ... >> { using type = tuple < common_type_t < remove_reference_t < Ts >&& , Ts > ... > ; }; template < class ... Ts , template < class > class TQual , template < class > class UQual > requires ( requires { typename common_reference_t < TQual < Ts > , UQual < remove_reference_t < Ts >&&>> ; } && ...) struct basic_common_reference < tuple < remove_reference_t < Ts >&& ... > , std :: tuple < Ts ... > , TQual , UQual > { using type = tuple < common_reference_t < TQual < remove_reference_t < Ts >&&> , UQual < Ts >> ... > ; }; template < class ... Ts , template < class > class TQual , template < class > class UQual > requires ( requires { typename common_reference_t < TQual < Ts > , UQual < remove_reference_t < Ts >&&>> ; } && ...) struct basic_common_reference < std :: tuple < Ts ... > , tuple < remove_reference_t < Ts >&& ... > , TQual , UQual > { using type = tuple < common_reference_t < TQual < Ts > , UQual < remove_reference_t < Ts >&&>> ... > ; }; template < template < class > class TQual , template < class > class UQual > struct basic_common_reference < tuple <> , tuple <> , TQual , UQual > { using type = tuple <> ; }; 
4.1.2.1. Ensure tuple pair constexpr 
   To ensure that the 
4.1.3. Example implementation
The following implementation has been taken and modified from cmcstl2:
template < View ... Rs > struct zip_view : view_interface < zip_view < Rs ... >> { private : // begin exposition-only template < bool Const , class Indices > struct __zipperator ; template < bool Const , class Indices > struct __sentinel ; static constexpr bool all_simple = ( simple - view < Rs > && ...); static constexpr bool all_forward = ( ForwardRange < Rs > && ...); static constexpr bool all_forward_const = ( ForwardRange < const Rs > && ...); static constexpr bool all_sized = ( SizedRange < Rs > && ...); static constexpr bool all_sized_const = ( SizedRange < const Rs > && ...); tuple < Rs ... > ranges_ {}; public : // end exposition-only zip_view () = default ; constexpr zip_view ( Rs ... rs ) noexcept (( is_nothrow_move_constructible_v < Rs > && ...)) requires sizeof ...( Rs ) != 0 constexpr auto begin (); constexpr auto begin () const requires all_forward_const ; constexpr auto end () requires all_forward ; constexpr auto end () const ; constexpr auto size () requires all_sized ; constexpr auto size () const requires all_sized_const ; }; template < class ... Rs > zip_view ( Rs && ...) -> zip_view < all_view < Rs > ... > ; 
4.1.3.1. zip_view 
constexpr zip_view ( Rs ... rs ) noexcept ( is_nothrow_move_constructible_v < Rs > && ...) requires ( sizeof ...( Rs ) != 0 ); 
- 
     Effects: Initialises ranges_ rs ... 
4.1.3.2. zip_view 
constexpr auto begin (); constexpr auto begin () const requires all_forward_const ; 
- 
     Effects: Returns an iterator containing a tuple of iterators to the beginning of each range, such that the first iterator corresponds to the beginning of the first range, the second tuple corresponds to the beginning of the second range, and so on. 
4.1.3.3. zip_view 
constexpr auto end () requires all_forward ; constexpr auto end () const ; 
- 
     Effects: Returns a sentinel containing a tuple of sentinels to the end of each range, such that the first sentinel corresponds to the end of the first range, the second sentinel corresponds to the end of the second range, and so on. 
constexpr auto size () requires all_sized ; constexpr auto size () requires all_sized_const ; 
- 
     Effects: Equivalent to returning the size of the smallest range in ranges_ 
4.1.3.4. zip_view < Rs ... > 
template < View ... Rs > template < bool Const , size_t ... Is > struct zip_view < Rs ... >:: __zipperator < Const , index_sequence < Is ... >> { private : template < class R > using maybe_const = conditional_t < Const , R const , R > ; static constexpr bool all_random_access = ( RandomAccessRange < maybe_const < Rs >> && ...); static constexpr bool all_bidi = ( BidirectionalRange < maybe_const < Rs >> && ...); static constexpr bool all_forward = ( ForwardRange < maybe_const < Rs >> && ...); static constexpr bool all_input = ( InputRange < maybe_const < Rs >> && ...); static_assert ( ! Const || all_forward ); static_assert ( Same < index_sequence_for < Rs ... > , index_sequence < Is ... >> ); zip_view < Rs ... >* parent_ = nullptr ; tuple < iterator_t < maybe_const < Rs >> ... > iterators_ {}; public : using value_type = tuple < iter_value_t < iterator_t < maybe_const < Rs >>> ... > ; using iterator_category = see - below ; using difference_type = see - below ; __zipperator () = default ; explicit constexpr __zipperator ( Parent & parent ); constexpr __zipperator ( Parent & parent ) requires all_forward ; constexpr __zipperator ( Parent & parent , difference_type n ) requires all_random_access ; constexpr auto operator * (); constexpr auto operator * () const requires ( dereferenceable < const iterator_t < maybe_const < Rs >>> && ...); constexpr __zipperator & operator ++ (); constexpr auto operator ++ ( int ); constexpr __zipperator & operator -- () requires all_bidi ; constexpr __zipperator operator -- ( int ) requires all_bidi ; friend constexpr bool operator == ( const __zipperator & x , const __zipperator & y ) requires all_forward ; friend constexpr bool operator != ( const __zipperator & x , const __zipperator & y ) requires all_forward ; friend constexpr bool operator == ( const __zipperator & x , default_sentinel ) requires ! all_forward ; friend constexpr bool operator == ( default_sentinel y , const __zipperator & x ) requires ! all_forward ; friend constexpr bool operator != ( const __zipperator & x , default_sentinel y ) requires ! all_forward ; friend constexpr bool operator != ( default_sentinel y , const __zipperator & x ) requires ! all_forward ; friend constexpr bool operator == ( const __zipperator & x , const Sent & y ) requires all_forward ; friend constexpr bool operator == ( const Sent & y , const __zipperator & x ) requires all_forward ; friend constexpr bool operator != ( const __zipperator & x , const Sent & y ) requires all_forward ; friend constexpr bool operator != ( const Sent & y , const __zipperator & x ) requires all_forward ; friend constexpr bool operator < ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; friend constexpr bool operator > ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; friend constexpr bool operator <= ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; friend constexpr bool operator >= ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; constexpr __zipperator & operator += ( difference_type n ) requires all_random_access ; friend constexpr __zipperator operator + ( const __zipperator & x , difference_type n ) requires all_random_access ; friend constexpr __zipperator operator + ( difference_type n , const __zipperator & x ) requires all_random_access ; constexpr __zipperator & operator -= ( difference_type n ) requires all_random_access ; friend constexpr __zipperator operator - ( const __zipperator & x , difference_type n ) requires all_random_access ; friend constexpr difference_type operator - ( const __zipperator & x , const __zipperator & y ) requires ( SizedSentinel < iterator_t < maybe_const < Rs >> , iterator_t < maybe_const < Rs >>> && ...); constexpr auto operator []( difference_type n ) const requires all_random_access ; friend constexpr auto iter_move ( const __zipperator & x ) requires (( Readable < iterator_t < maybe_const < Rs >>> && ...)); friend constexpr void iter_swap ( const __zipperator & x , const __zipperator & y ); requires ( IndirectlySwappable < iterator_t < maybe_const < Rs >>> && ...) }; 
4.1.3.5. zip_view :: __zipperator 
using iterator_category = see - below ; 
- 
     Effects: If ( RandomAccessRange < Rs > && ...) iterator_category random_access_iterator_tag ( BidirectionalRange < Rs > && ...) iterator_category bidirectional_iterator_tag ( ForwardRange < Rs > && ...) iterator_category forward_iterator_category ( InputRange < Rs > && ...) iterator_category input_iterator_tag iterator_category output_iterator_category 
using difference_type = see - below ; 
- 
     Effects: If sizeof ...( Rs ) == 0 difference_type int difference_type common_type_t < iterator_t < conditional_t < Const , const Rs , Rs >> ... > 
4.1.3.6. zip_view :: __zipperator 
explicit constexpr __zipperator ( Parent & parent ); 
- 
     Effects: Initialises parent_ std :: addressof ( parent ) 
constexpr __zipperator ( Parent & parent ) requires all_forward ; 
- 
     Effects: Initialises parent_ std :: addressof ( parent ) iterators_ 
constexpr __zipperator ( Parent & parent , difference_type n ) requires all_random_access ; 
- 
     Effects: Initiaises parent_ std :: addressof ( parent ) iterators_ r Rs ... n > size ( r ) 
4.1.3.7. zip_view :: __zipperator 
constexpr auto operator * (); constexpr auto operator * () const ; 
- 
     Effects: Returns a tuple of references to the elements of each range denoted by * i __zipperator 
constexpr auto operator []( difference_type n ) const requires all_random_access ; 
- 
     Effects: Equivalent to return * ( * this + n ); 
4.1.3.8. zip_view :: __zipperator 
constexpr __zipperator & operator ++ (); 
- 
     Effects: Equivalent to incrementing each iterator in iterators_ 
- 
     Returns: * this 
constexpr auto operator ++ ( int ); 
- 
     Effects: Equivalent to: 
if constexpr ( all_forward ) { auto temp = * this ; ++* this ; return temp ; } else { ++* this ; } 
constexpr __zipperator & operator -- () requires all_bidi ; 
- 
     Effects: Equivalent to decrementing each iterator in iterators_ 
constexpr __zipperator operator -- ( int ) requires all_bidi ; 
- 
     Effects: Equivalent to: 
auto temp = * this ; --* this ; return temp ; 
constexpr __zipperator & operator += ( difference_type n ) requires all_random_access ; 
- 
     Effects: Equivalent to calling ranges :: advance ( E , n ) iterators_ E 
constexpr __zipperator & operator -= ( difference_type n ) requires all_random_access ; 
- 
     Effects: Equivalent to return * this += - n ; 
4.1.3.9. zip_view :: __zipperator 
friend constexpr bool operator == ( const __zipperator & x , const __zipperator & y ) requires all_forward ; 
- 
     Effects: Equivalent to return x . iterators_ == y . iterators_ ; 
friend constexpr bool operator == ( const __zipperator & x , default_sentinel ) requires ! all_forward ; friend constexpr bool operator == ( default_sentinel y , const __zipperator & x ) requires ! all_forward ; 
- 
     Effets: Returns trueif at least one iterator initerators_ falseotherwise.
friend constexpr bool operator == ( const __zipperator & x , const Sent & y ) requires all_forward ; friend constexpr bool operator == ( const Sent & y , const __zipperator & x ) requires all_forward ; 
- 
     Effects: Returns trueif at least one iterator initerators_ 
friend constexpr bool operator != ( const __zipperator & x , const __zipperator & y ) requires all_forward ; friend constexpr bool operator != ( const __zipperator & x , default_sentinel y ) requires ! all_forward ; friend constexpr bool operator != ( default_sentinel y , const __zipperator & x ) requires ! all_forward ; friend constexpr bool operator != ( const __zipperator & x , const Sent & y ) requires all_forward ; friend constexpr bool operator != ( const Sent & y , const __zipperator & x ) requires all_forward ; 
- 
     Effects: Equivalent to return ! ( x == y ); 
friend constexpr bool operator < ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; 
- 
     Effects: Equivalent to return x . iterators_ < y . iterators_ ; 
friend constexpr bool operator > ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; 
- 
     Effects: Equivalent to return y < x ; 
friend constexpr bool operator <= ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; 
- 
     Effects: Equivalent to return ! ( y < x ); 
friend constexpr bool operator >= ( const __zipperator & x , const __zipperator & y ) requires all_random_access ; 
- 
     Effects: Equivalent to return ! ( x < y ); 
4.1.3.10. zip_view < Rs ... >:: __zipperator 
friend constexpr __zipperator operator + ( const __zipperator & x , difference_type n ) requires all_random_access ; friend constexpr __zipperator operator + ( difference_type n , const __zipperator & x ) requires all_random_access ; 
- 
     Effects: Equivalent to: 
auto temp = x ; temp += n ; return temp ; 
friend constexpr __zipperator operator - ( const __zipperator & x , difference_type n ) requires all_random_access ; 
- 
     Effects: Equivalent to return x + - n ; 
friend constexpr difference_type operator - ( const __zipperator & x , const __zipperator & y ) requires ( SizedSentinel < iterator_t < maybe_const < Rs >> , iterator_t < maybe_const < Rs >>> && ...) 
- 
     Effects: If sizeof ...( Rs ) == 0 x . iterators_ y . iterators_ 
4.1.3.11. zip_view < Rs ... >:: __zipperator 
friend constexpr auto iter_move ( const __zipperator & x ) requires (( Readable < iterator_t < maybe_const < Rs >>> && ...)); 
- 
     Effects: Returns a tuple containing expressions equivalent to iter_move x . iterators_ 
4.1.3.12. zip_view < Rs ... >:: __zipperator 
friend constexpr void iter_swap ( const __zipperator & x , const __zipperator & y ) requires ( IndirectlySwappable < iterator_t < maybe_const < Rs >>> && ...); 
- 
     Effects: Equivalent to iter_swap x . iterators_ y . iterators_ 
4.1.3.13. zip_view < Rs ... > 
template < View ... Rs > template < bool Const , size_t ... Is > struct zip_view < Rs ... >:: __sentinel < Const , index_sequence < Is ... >> { private : using Iter = typename zip_view < Rs ... >:: template __zipperator < Const , index_sequence < Is ... >> ; friend Iter ; using Parent = conditional_t < Const , const zip_view < Rs ... > , zip_view < Rs ... >> ; static constexpr bool all_forward = ( ForwardRange < maybe_const < Rs >> && ...); static_assert ( all_forward ); static_assert ( Same < index_sequence_for < Rs ... > , index_sequence < Is ... >> ); tuple < sentinel_t < maybe_const < Rs >> ... > last_ ; public : __sentinel () = default ; explicit constexpr __sentinel ( Parent & parent ); }; 
4.2. view :: zip 
   The name 
- 
     zip_view { rs ...} ( Range < Rs > && ...) && ( is_object_v < Rs > && ...) 
- 
     Otherwise view :: zip ( rs ...) 
4.3. basic_istream_view 
   4.3.1. Motivation
| Without  | With  | 
|---|---|
| 
 | 
 | 
The problem with 
auto v = vector ( ranges :: istream_view < int > { cin }); // ... copy ( ranges :: istream_view < int > { cin }, back_inserter ( v )); 
This code is cleaner: we are implicitly saying "until our 
4.3.2. Interface
template < class T , class CharT = char , class Traits = char_traits < CharT >> concept stream - extractable = see below ; // exposition only template < Semiregular Val , class CharT , class Traits = char_traits < CharT >> requires stream - extractable < Val , CharT , Traits > class basic_istream_view : public view_interface < basic_istream_view < Val , CharT , Traits >> { public : basic_istream_view () = default ; explicit constexpr basic_istream_view ( basic_istream < CharT , Traits >& stream ); constexpr auto begin (); constexpr default_sentinel end () const noexcept ; private : struct __iterator ; // exposition-only basic_istream < CharT , Traits >* stream_ ; // exposition-only Val object_ ; // exposition-only }; template < class T , class CharT , class Traits > basic_istream_view < T , CharT , Traits > istream_view ( basic_istream < CharT , Traits >& in ); 
4.3.2.1. Exposition-only concept stream - extractable 
   The exposition-only concept 
template < class T , class CharT = char , class Traits = char_traits < CharT >> concept stream - extractable = // exposition only requires ( basic_istream < CharT , Traits >& is , T & t ) { { is >> t } -> Same < basic_istream < CharT , Traits >>& ; }; 
- 
     Remarks: std :: addressof ( is ) == std :: addressof ( is << t ) 
4.3.2.2. basic_istream_view 
explicit constexpr basic_istream_view ( basic_istream < CharT , Traits >& stream ); 
- 
     Effects: Initialises stream_ std :: addressof ( stream ) 
4.3.2.3. basic_istream_view 
constexpr auto begin (); 
- 
     Effects: Equivalent to 
* stream_ >> object_ ; return __iterator { * this }; 
4.3.2.4. basic_istream_view 
constexpr default_sentinel end () const noexcept ; 
- 
     Returns: default_sentinel {} 
4.3.2.5. basic_istream_view :: __iterator 
template < class Val , class CharT , class Traits > class basic_istream_view < Val , CharT , Traits >:: __iterator { public : using iterator_category = input_iterator_tag ; using difference_type = ptrdiff_t ; using value_type = Val ; __iterator () = default ; explicit constexpr __iterator ( basic_istream_view < Val >& parent ) noexcept ; __iterator & operator ++ (); void operator ++ ( int ); Val & operator * () const ; friend bool operator == ( __iterator x , default_sentinel ); friend bool operator == ( default_sentinel y , __iterator x ); friend bool operator != ( __iterator x , default_sentinel y ); friend bool operator != ( default_sentinel y , __iterator x ); private : basic_istream_view < Val , CharT , Traits >* parent_ = nullptr ; // exposition-only }; 
4.3.2.6. basic_istream_view :: __iterator 
explicit constexpr __iterator ( basic_istream_view < Val >& parent ) noexcept ; 
- 
     Effects: Initialises parent_ std :: addressof ( parent_ ) 
4.3.2.7. basic_istream_view :: __iterator 
__iterator & operator ++ (); void operator ++ ( int ); 
- 
     Effects: Equivalent to 
* parent_ -> stream_ >> parent_ -> object_ ; 
4.3.2.8. basic_istream_view :: __iterator 
Val & operator * () const ; 
- 
     Effects: Equivalent to return parent_ -> value_ ; 
4.3.2.9. basic_istream_view :: __iterator 
friend bool operator == ( __iterator x , default_sentinel ); 
- 
     Effects: Equivalent to return !* x . parent_ -> stream_ ; 
friend bool operator == ( default_sentinel y , __iterator x ); 
- 
     Returns: x == y 
friend bool operator != ( __iterator x , default_sentinel y ); 
- 
     Returns: ! ( x == y ) 
friend bool operator != ( default_sentinel y , __iterator x ); 
- 
     Returns: ! ( x == y ) 
4.3.2.10. basic_istream_view 
template < class T , class CharT , class Traits > basic_istream_view < T , CharT , Traits > istream_view ( basic_istream < CharT , Traits >& s ); 
- 
     Effects: Equivalent to return basic_istream_view < T , CharT , Traits > { s }; 
4.4. Introducing associated types for ranges
Note: This section was formerly titled Reviewing 
[P1037] introduced 
-  Use the traditional typename T :: value_type 
- 
      Define our own associated type à la 
template < class T > requires requires { typename T :: value_type ; } using value_type = typename T :: value_type ; This was apparently possible in the Ranges TS (albeit in much more detail) [iter.assoc.types.value_type], but P1037 has redefined value_type_t iter_value_t value_type R :: value_type R :: iterator :: value_type vector < int const >:: value_type vector < int const >:: iterator :: value_type int const int 
-  Use iter_value_t < iterator_t < R >> 
When discussing extending 
What shouldbe wheniter_value_t < T > is an iterator with value typeT and a range with value typeU ?V Alternately: What is
?iter_value_t < array < const int >> isarray < const int >:: value_type , butconst int isiter_value_t < iterator_t < array < const int >> .int Specific examples aside, early attempts to make the associated type aliases "polymorphic" kept running into ambiguities that we had to disambiguate or simply declare broken.
I’d rather see
et al proposed.range_value_t 
P1035 introduces shorthands for 
template < class R > using range_difference_t = iter_difference_t < iterator_t < R >> ; template < class R > using range_value_t = iter_value_t < iterator_t < R >> ; template < class R > using range_reference_t = iter_reference_t < iterator_t < R >> ; template < class R > using range_rvalue_reference_t = iter_rvalue_reference_t < iterator_t < R >> ; 
These have an enormous amount of usage in range-v3, and will ensure consistency between generic code that uses ranges and generic code that uses iterators (which are essential for underpinning all range abstractions in C++).
4.4.1. Amendment to the associated iterator types
The previous revision of P1035 imposed explicit 
Since the "Better Safer Range Access CPOs" work was merged, the definition of
no longer depends onRange anditerator_t ; they could be constrained to requiresentinel_t . If that were the case, theRange aliases wouldn’t need to be so constrained, because they would already be ill-formed when passed a non-range_foo_t = foo < iterator_t < X >> .Range We’re big on pushing requirements into the "leaves" of the design to put errors closer to the user and as early as possible, seems like a reasonable change to me.
We couldn’t do it in the TS era, because
depended onRange anditerator_t - but that’s no longer the case.sentinel_t 
P1035 proposes that 
- template<class T>
+ template<Range T>
    using iterator_t = decltype(ranges::begin(declval<T&>()));
- template<class T>
+ template<Range T>
    using sentinel_t = decltype(ranges::end(declval<T&>()));
   4.5. take_while_view 
   4.5.1. Motivation
P0789 introduces 
auto v = vector { 0 , 1 , 2 , 3 , 4 , 5 }; cout << distance ( v | view :: take ( 3 )) << '\n' ; // prints 3 copy ( v | view :: take ( 3 ), ostream_iterator < int > ( cout , " " )); // prints 0 1 2 copy ( v | view :: take ( distance ( v )), ostream_iterator < int > ( cout , " " )); // prints 0 1 2 3 4 5 
| Current | Proposed | 
|---|---|
| 
 | 
 | 
| Wandbox demo | Wandbox demo | 
The former requires that a user define their own sentinel type: something that while not expert-friendly, is yet to be established as a widespread idiom in C++, and providing a range adaptor for this purpose will help programmers determine when a sentinel is _not_ necessary.
4.5.2. Notes
- 
     There is a slight programmer overhead in the naming: the author felt that both is_odd_sentinel is_even_sentinel is_odd_sentinel is_even_sentinel 
- 
     A sentinel that takes a lambda may be of interest to LEWG. If there is interest in this, a proposal could be made in the C++23 timeframe. 
4.5.3. Interface and specification
template < View R , class Pred > requires InputRange < R > && is_object_v < Pred > && IndirectUnaryPredicate < const Pred , iterator_t < R >> class take_while_view : public view_interface < take_while_view < R , Pred >> { template < bool > class __sentinel ; // exposition-only public : take_while_view () = default ; constexpr take_while_view ( R base , Pred pred ); template < ViewableRange O > requires constructible - from - range < R , O > constexpr take_while_view ( O && o , Pred pred ); constexpr R base () const ; constexpr const Pred & pred () const ; constexpr auto begin () requires ( ! simple - view < R > ); constexpr auto begin () const requires Range < const R > ; constexpr auto end () requires ( ! simple - view < R > ); constexpr auto end () const requires Range < const R > ; private : R base_ ; // exposition-only semiregular < Pred > pred_ ; // exposition-only }; template < class R , class Pred > take_while_view ( R && , Pred ) -> take_while_view < all_view < R > , Pred > ; 
4.5.3.1. Post-San Diego notes
A remark was made in San Diego that 
4.5.3.2. take_while_view 
constexpr take_while_view ( R base , Pred pred ); 
- 
     Effects: Initialises base_ base pred_ pred 
template < ViewableRange O > requires constructible - from - range < R , O > constexpr take_while_view ( O && o , Pred pred ); 
- 
     Effects: Initialises base_ view :: all ( std :: forward < O > ( o )) pred_ pred 
4.5.3.3. take_while_view 
constexpr R base () const ; 
- 
     Returns: base_ 
constexpr const Pred & pred () const ; 
- 
     Returns: pred_ 
4.5.3.4. take_while_view 
constexpr auto begin () requires ( ! simple - view < R > ); constexpr auto begin () const requries Range < const R > 
- 
     Effects: Equivalent to return ranges :: begin ( base_ ); 
4.5.3.5. take_while_view 
constexpr auto end () requires ( ! simple - view < R > ); constexpr auto end () const requires Range < const R > ; 
- 
     Effects: Equivalent to return __sentinel < is_const_v < decltype ( * this ) >> ( & pred ()); 
4.5.4. take_while_view :: __sentinel 
template < class R , class Pred > template < bool Const > class take_while_view < R , Pred >:: __sentinel { using Parent = conditional_t < Const , const take_while_view , take_while_view > ; using Base = conditional_t < Const , const R , R > ; sentinel_t < Base > end_ {}; // exposition-only const Pred * pred_ {}; // pred public : __sentinel () = default ; constexpr explicit __sentinel ( sentinel_t < Base > end , const Pred * pred ); constexpr __sentinel ( __sentinel <! Const > s ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < Base >> constexpr sentinel_t < Base > base () const { return end_ ; } friend constexpr bool operator == ( const __sentinel & x , const iterator_t < Base >& y ); friend constexpr bool operator == ( const iterator_t < Base >& x , const __sentinel & y ); friend constexpr bool operator != ( const __sentinel & x , const iterator_t < Base >& y ); friend constexpr bool operator != ( const iterator_t < Base >& x , const __sentinel & y ); }; 
4.5.5. take_while_view :: __sentinel 
constexpr explicit __sentinel ( sentinel_t < Base > end , const Pred * pred ); 
- 
     Effects: Initialises end_ end pred_ pred 
constexpr __sentinel ( __sentinel <! Const > s ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < Base >> ; 
- 
     Effects Initialises end_ s . end_ pred_ s . pred_ 
4.5.6. take_while_view :: __sentinel 
constexpr sentinel_t < Base > base () const ; 
- 
     Effects: Equivalent to return end_ ; 
4.5.7. take_while_view :: __sentinel 
friend constexpr bool operator == ( const __sentinel & x , const iterator_t < Base >& y ) 
- 
     Effects: Equivalent to return x . end_ != y && ! ( * x . pred_ )( * y ); 
friend constexpr bool operator == ( const iterator_t < Base >& x , const __sentinel & y ); 
- 
     Effects: Equivalent to return y == x ; 
friend constexpr bool operator != ( const __sentinel & x , const iterator_t < Base >& y ); 
- 
     Effects: Equivalent to ! ( x == y ); 
friend constexpr bool operator != ( const iterator_t < Base >& x , const __sentinel & y ); 
- 
     Effects: Equivalent to ! ( y == x ); 
4.6. view :: take_while 
   The name 
- 
     take_while_view { E , F } T InputRange F IndirectUnaryPredicate 
- 
     Otherwise ranges :: view :: take_while ( E , F ) 
4.7. drop_view 
   4.7.1. Motivation
| Current (C++17) | Proposed (C++20) | 
|---|---|
| 
 | 
 | 
4.7.2. Interface
template < View R > class drop_view : public view_interface < drop_view < R >> { using D = iter_difference_t < iterator_t < R >> ; // exposition-only public : drop_view (); constexpr drop_view ( R base , D count ); template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_view ( O && o , D count ); constexpr R base () const ; constexpr auto begin () requires ( ! ( simple - view < R > && RandomAccessRange < R > )); constexpr auto begin () const requires Range < const R > && RandomAccessRange < const R > ; constexpr auto end () requires ( ! simple - view < R > ); constexpr auto end () const requires Range < const R > ; constexpr auto size () requires ( ! simple - view < R > ) && SizedRange < R > ; constexpr auto size () const requires SizedRange < const R > ; private : R base_ ; // exposition-only D count_ ; // exposition-only }; template < Range R > drop_view ( R && , iter_difference_t < iterator_t < R >> ) -> drop_view < all_view < R >> ; 
4.7.2.1. drop_view 
constexpr drop_view ( R base , D count ); 
- 
     Effects: Initialises base_ base count_ count 
- 
     Expects: 0 < count 
template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_view ( O && o , D count ); 
- 
     Effects: Initialises base_ view :: all ( std :: forward < O > ( o )) count_ count 
- 
     Expects: 0 < count 
4.7.2.2. drop_view 
constexpr R base () const ; 
- 
     Effects: Equivalent to return base_ 
4.7.2.3. drop_view 
constexpr auto begin () requires ( ! ( simple - view < R > && RandomAccessRange < R > )); constexpr auto begin () const requires Range < const R > && RandomAccessRange < const R > ; 
- 
     Effects: Equivalent to return ranges :: next ( ranges :: begin ( base_ ), count_ , ranges :: end ( base_ )); 
- 
     Remarks: In order to provide the amortized constant time complexity required by the Range concept, the first overload caches the result within the drop_view 
4.7.2.4. drop_view 
constexpr auto end () requires ( ! simple - view < R > ); constexpr auto end () const requires Range < const R > ; 
- 
     Effects: Equivalent to return ranges :: end ( base_ ); 
4.7.2.5. drop_view 
constexpr auto size () requires ( ! simple - view < R > ) && SizedRange < R > ; constexpr auto size () const requires SizedRange < const R > ; 
- 
     Equivalent to: 
auto const size = ranges :: size ( base_ ); auto const count = static_cast < decltype ( size ) > ( count_ ); return size < count ? 0 : size - count ; 
4.8. view :: drop 
   The name 
- 
     drop_view { E , F } T InputRange F iter_difference_t < iterator_t < T >> 
- 
     Otherwise view :: drop ( E , F ) 
4.9. drop_while_view 
   4.9.1. Motivation
The motivation for 
| Current (C++17) | Proposed (C++20) v1 | 
|---|---|
| 
 | 
 | 
4.9.2. Interface
template < View R , class Pred > requires InputRange < R > && is_object_v < Pred > && IndirectUnaryPredicate < const Pred , iterator_t < R >> class drop_while_view : public view_interface < drop_while_view < R , Pred >> { public : drop_while_view () = default ; constexpr drop_while_view ( R base , Pred pred ); template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_while_view ( O && o , Pred pred ); constexpr R base () const ; constexpr Pred pred () const ; constexpr auto begin (); constexpr auto end (); private : R base_ ; // exposition-only semiregular < Pred > pred_ ; // exposition-only }; template < class R , class Pred > drop_while_view ( R && , Pred ) -> drop_while_view < all_view < R > , Pred > ; 
4.9.2.1. drop_while_view 
constexpr drop_while_view ( R base , Pred pred ); 
- 
     Effects: Initialises base_ base pred_ pred 
template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_while_view ( O && o , Pred pred ) 
- 
     Effects: Initialises base_ view :: all ( std :: forward < O > ( o )) pred_ pred 
4.9.2.2. drop_while_view 
constexpr R base () const ; 
- 
     Returns: base_ 
constexpr Pred pred () const ; 
- 
     Returns: pred_ 
4.9.2.3. drop_while_view 
constexpr auto begin (); 
- 
     Effects: Equivalent to return ranges :: find_if_not ( base_ , std :: ref ( pred_ )); 
- 
     Remarks: In order to provide the amortized constant time complexity required by the Range drop_while_view reverse_view drop_view 
4.9.2.4. drop_while_view 
   - 
     Effects: Equivalent to return ranges :: end ( base_ ); 
4.10. view :: drop_while 
   The name 
- 
     drop_while_view { X , Y } T InputRange Y F IndirectUnaryPredicate 
- 
     Otherwise view :: drop_while ( E , F ) 
4.11. keys values 
   4.11.1. Motivation
It is frequent to want to iterate over the keys or the values of an associative container. There is
currently no easy way to do that. It can be approximated using 
| Current | Proposed | 
|---|---|
| 
 | 
 | 
These views have been part of ranges-v3 and we also have implemented them in cmcstl2. A lot of
languages and frameworks (notably JS, Python, Qt, Java, [Boost.Range]) offer methods to extract
the keys or values of an associative container, often through 
std :: vector < std :: tuple < int , int , int >> vector_of_tuple = { { 0 , 1 , 2 }, { 3 , 4 , 5 }, { 6 , 7 , 8 }, { 9 , 10 , 11 } }; lst = view :: elements < 2 > ( vector_of_tuple ); //lst == {2, 5, 8, 11} 
- 
     keys_view view :: keys 
It can be defined as:
template < InputRange R > requires std :: tuple_size_v < iter_value_t < iterator_t < R >>> == 2 using keys_view = elements_view < R , 0 > ; 
- 
     values_view view :: values 
It can be defined as:
template < InputRange R > requires std :: tuple_size_v < iter_value_t < iterator_t < R >>> == 2 using values_view = elements_view < R , 1 > ; 
And used as:
auto total_zeroes = ranges :: count ( map | ranges :: view :: values , 0 ); 
4.11.2. Interface
namespace ranges { template < InputRange R , size_t N > requries View < R > && N < tuple_size_v < range_value_t < R >> class elements_view : public view_interface < elements_view < R , N >> { private : template < bool Const > struct __iterator ; // exposition-only template < bool Const > struct __sentinel ; // exposition-only R base_ ; // exposition-only public : elements_view () = default ; constexpr explicit elements_view ( R base ); constexpr R base () const noexcept ; constexpr auto begin () requires ( ! simple - view < const R > ); constexpr auto begin () const requires simple - view < const R > ; constexpr auto end () requires ( ! simple - view < const R > ); constexpr auto end () const requires simple - view < const R > ; constexpr auto size () const requires SizedRange < R > && ( ! simple - view < const R > ); constexpr auto size () const requires SizedRange < const R > && simple - view < const R > ; }; template < class R > using keys_view = elements_view < all_view < R > , 0 > ; template < class R > using values_view = elements_view < all_view < R > , 1 > ; 
4.11.2.1. elements_view 
constexpr explicit elements_view ( R base ); 
- 
     Effects: Initialises base_ base 
4.11.2.2. elements_view 
constexpr R base () const noexcept ; 
- 
     Effects: Equivalent to return base_ ; 
4.11.2.3. elements_view 
constexpr auto begin () requires ( ! simple - view < const R > ); constexpr auto begin () const requires simple - view < const R > ; 
- 
     Effects: Equivalent to return __iterator < is_const_v < decltype ( * this ) >> ( * this , begin ( base_ )); 
4.11.2.4. elements_view 
constexpr auto end () requires ( ! simple - view < const R > ); constexpr auto end () const requires simple - view < const R > ; 
- 
     Effects: Equivalent to return __iterator < is_const_v < decltype ( * this ) >> ( * this , end ( base_ )); 
4.11.2.5. elements_view 
constexpr auto size () const requires SizedRange < R > && ( ! simple - view < const R > ); constexpr auto size () const requires SizedRange < const R > && simple - view < const R > ; 
- 
     Effects: Equivalent to return size ( base_ ); 
4.11.3. elements_view < R ,  N > 
template < class R , std :: size_t N > template < bool Const > class elements_view < R , N >:: __iterator { // exposition-only private : using __parent = conditional_t < Const , const elements_view , elements_view > ; using __base = conditional_t < Const , const R , R > ; friend __iterator <! Const > ; friend __sentinel < Const > ; __parent * parent_ = nullptr ; iterator_t < __base > current_ {}; public : using iterator_category = iterator_category_t < iterator_t < Base >> ; using value_type = remove_cvref_t < tuple_element_t < N , range_value_t < __base >>> ; using difference_type = range_difference_t < __base > ; __iterator () = default ; constexpr explicit __iterator ( __parent & parent , iterator_t < __base > current ); constexpr explicit __iterator ( __iterator <! Const > i ) requires Const && ConvertibleTo < iterator_t < R > , iterator_t < __base >> ; constexpr iterator_t < Base > base () const noexcept ; constexpr decltype ( auto ) operator * () const ; constexpr __iterator & operator ++ (); constexpr void operator ++ ( int ) requires ( ! ForwardRange < Base > ); constexpr __iterator operator ++ ( int ) requires ForwardRange < Base > ; constexpr __iterator & operator -- () requires BidirectionalRange < Base > ; constexpr __iterator operator -- ( int ) requires BidirectionalRange < Base > ; constexpr __iterator operator += ( difference_type n ) requires RandomAccessRange < Base > ; constexpr __iterator operator -= ( difference_type n ) requires RandomAccessRange < Base > ; constexpr decltype ( auto ) operator []( difference_type n ) requires RandomAccessRange < Base > ; constexpr friend bool operator == ( const __iterator & x , const __iterator & y ) requires EqualityComparable < iterator_t < Base >> ; constexpr friend bool operator != ( const __iterator & x , const __iterator & y ) requires EqualityComparable < iterator_t < Base >> ; constexpr friend bool operator < ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; constexpr friend bool operator <= ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; constexpr friend bool operator >= ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; constexpr friend bool operator > ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; constexpr friend __iterator operator + ( const __iterator & x , const difference_type & y ) requires RandomAccessRange < Base > ; constexpr friend __iterator operator + ( const difference_type & x , const __iterator & x ) requires RandomAccessRange < Base > ; constexpr friend __iterator operator - ( const __iterator & x , const difference_type & y ) requires RandomAccessRange < Base > ; constexpr friend difference_type operator - ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; }; 
4.11.3.1. elements_view < R ,  N >:: __iterator 
constexpr explicit __iterator ( __parent & parent , iterator_t < __base > current ); 
- 
     Effects: Initialises parent_ std :: addressof ( parent ) current_ current 
constexpr explicit __iterator ( __iterator <! Const > i ) requires Const && ConvertibleTo < iterator_t < R > , iterator_t < __base >> ; 
- 
     Effects: Initialises parent_ i . parent_ current_ i . current_ 
4.11.3.2. elements_view < R ,  N >:: __iterator 
constexpr iterator_t < Base > base () const noexcept ; 
- 
     Effects: Equivalent to return current_ ; 
4.11.3.3. elements_view < R ,  N >:: __iterator 
constexpr __iterator & operator ++ (); 
- 
     Effects: Equivalent to ++ current_ ; return * this ; 
constexpr void operator ++ ( int ) requires ( ! ForwardRange < Base > ); 
- 
     Effects: Equivalent to ++ current_ ; 
constexpr __iterator operator ++ ( int ) requires ForwardRange < Base > ; 
- 
     Effects: Equivalent to 
auto temp = * this ; ++ current_ ; return temp ; 
4.11.3.4. elements_view < R ,  N >:: __iterator 
constexpr __iterator & operator -- () requires BidirectionalRange < Base > ; 
- 
     Effects: Equivalent to -- current_ ; return * this ; 
constexpr __iterator operator -- ( int ) requires BidirectionalRange < Base > ; 
- 
     Effects: Equivalent to 
auto temp = * this ; -- current_ ; return temp ; 
4.11.3.5. elements_view < R ,  N >:: __iterator 
constexpr __iterator operator += ( difference_type n ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to current_ += n ; return * this ; 
constexpr __iterator operator -= ( difference_type n ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to current_ -= n ; return * this ; 
constexpr friend __iterator operator + ( const __iterator & x , const difference_type & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to 
constexpr friend __iterator operator + ( const difference_type & x , const __iterator & x ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to 
constexpr friend __iterator operator - ( const __iterator & x , const difference_type & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to 
constexpr friend difference_type operator - ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to 
4.11.3.6. elements_view < R ,  N >:: __iterator 
constexpr decltype ( auto ) operator * () const ; 
- 
     Effects: Equivalent to return std :: get < N > ( * current_ ); 
constexpr decltype ( auto ) operator []( difference_type n ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to return * ( current_ + n ); 
4.11.3.7. elements_view < R ,  N >:: __iterator 
constexpr friend bool operator == ( const __iterator & x , const __iterator & y ) requires EqualityComparable < iterator_t < Base >> ; 
- 
     Effects: Equivalent to return x . current_ == y . current_ ; 
constexpr friend bool operator != ( const __iterator & x , const __iterator & y ) requires EqualityComparable < iterator_t < Base >> ; 
- 
     Effects: Equivalent to return ! ( x == y ) 
constexpr friend bool operator < ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to return x . current_ < y . current_ ; 
constexpr friend bool operator <= ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to return ! ( y < x ); 
constexpr friend bool operator >= ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to return ! ( x < y ); 
constexpr friend bool operator > ( const __iterator & x , const __iterator & y ) requires RandomAccessRange < Base > ; 
- 
     Effects: Equivalent to return y < x ; 
4.11.4. elements_view < R ,  N > 
template < class R , std :: size_t N > template < bool Const > class elements_view < R , N >:: __sentinel { // exposition-only private : using __parent = conditional_t < Const , const elements_view , elements_view > ; using __base = conditional_t < Const , const R , R > ; sentinel_t < __base > end_ {}; friend __sentinel <! Const > ; public : __sentinel () = default ; constexpr explicit __sentinel ( __sentinel < __base > end ); constexpr explicit __sentinel ( __sentinel <! Const > i ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < const R >> ; constexpr sentinel_t < Base > base () const ; constexpr friend bool operator == ( const __iterator < Const >& x , const __sentinel & y ); constexpr friend bool operator == ( const __sentinel x , const __iterator < Const >& y ); constexpr friend bool operator != ( const __iterator < Const >& x , const __sentinel & y ); constexpr friend bool operator != ( const __sentinel x , const __iterator < Const >& y ); friend constexpr range_difference_t < __base > operator - ( const __iterator < Const >& x , const __sentinel & y ) requires SizedSentinel < sentinel_t < __base > , iterator_t < __base >> ; friend constexpr range_difference_t < __base > operator - ( const __sentinel & x , const __iterator < Const >& y ) requires SizedSentinel < sentinel_t < __base > , iterator_t < __base >> ; }; 
4.11.4.1. elements_view < R ,  N >:: __sentinel 
constexpr explicit __sentinel ( __sentinel < __base > end ); 
- 
     Effects: Initialises end_ end 
constexpr explicit __sentinel ( __sentinel <! Const > i ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < const R >> ; 
- 
     Effects: Initialises end_ i . end 
4.11.4.2. elements_view < R ,  N >:: __sentinel 
constexpr sentinel_t < Base > base () const ; 
- 
     Effects: Equivalent to return base_ ; 
4.11.4.3. elements_view < R ,  N >:: __sentinel 
constexpr friend bool operator == ( const __iterator < Const >& x , const __sentinel & y ); 
- 
     Effects: Equivalent to return x . current_ == y . end_ ; 
constexpr friend bool operator != ( const __iterator < Const >& x , const __sentinel & y ); 
- 
     Effects: Equivalent to return ! ( x == y ); 
constexpr friend bool operator == ( const __sentinel x , const __iterator < Const >& y ); constexpr friend bool operator != ( const __sentinel x , const __iterator < Const >& y ); 
- 
     Effects: Equivalent to return y == x ; return y != x ; 
4.11.4.4. elements_view < R ,  N >:: __sentinel 
friend constexpr range_difference_t < __base > operator - ( const __iterator < Const >& x , const __sentinel & y ) requires SizedSentinel < sentinel_t < __base > , iterator_t < __base >> ; 
- 
     Effects: Equivalent to return x . current_ - y . end_ ; 
friend constexpr range_difference_t < __base > operator - ( const __sentinel & x , const __iterator < Const >& y ) requires SizedSentinel < sentinel_t < __base > , iterator_t < __base >> ; 
- 
     Effects: Equivalent to return - ( y - x ) 
4.11.5. view :: keys 
   The name 
- 
     keys_view { X } T InputRange range_value_t < T > pair - like 
- 
     Otherwise view :: keys ( X ) 
4.11.6. view :: values 
   The name 
- 
     values_view { X } T InputRange range_value_t < T > pair - like 
- 
     Otherwise view :: values ( X ) 
4.11.7. view :: elements 
   The name 
- 
     elements_view < all_view < T > , N > { X } T InputRange range_value_t < T > N < tuple_size_v < range_value_t < T >> 
- 
     Otherwise view :: elements ( X ) 
Editor’s note: the term tuple-like is used in a similar fashion to [range.subrange]'s pair-like, but for tuple-like entities. No wording exists as present.