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
and
keys_view (in addition).
values_view
SF | F | N | A | SA |
---|---|---|---|---|
0 | 10 | 3 | 0 | 0 |
We want to prioritize
and
keys () (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 is
zip_longest () or
tuple < 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
anditerator_t
requiresentinel_t
in their interface.Range -
Adjusts associated types for ranges so that they don’t explicitly require
(this is deferred toRange
).iterator_t -
Adjusts
andkeys_view
as per LEWG’s request.values_view
1.3. From R2 to R3
-
Adds polls from San Diego meeting.
-
Removed
andrange_size_t
from the associated types.range_common_iterator_t -
Added justification for why
is necessary foris_object_v
.take_while_view -
Replaced contract-specified pre-conditions with text-specified pre-conditions.
-
Removed concept
, as it is not relevant to the contents of this paper.StreamInsertable -
Replaced concept
with exposition-only conceptStreamExtractable
.stream - extractable -
This was done, in part, to balance the fact that a concept would exist for
but notoperator >>
.operator <<
-
-
Replaced pros and cons of
with__tuple_hack
-qualified overloads forconst
and necessarystd :: tuple
andcommon_type
specialisations.basic_common_type
1.3.1. Changes promised for R4
-
Apply additions to
tostd :: tuple
also.std :: pair -
Considering
.zip_longest -
Changes to
andkeys
.values
1.4. From R1 to R2
-
Expanded acknowledgements and co-authors.
-
Removed
.zip_with_view -
Added
.zip_view -
Added
andkeys
.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
in P1035R1. Due to
complications in the design process,
will not be proposed, but
shall be.
A subsubsection below articulates how to emulate
below.
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
will allow programmers to iterate over multiple ranges at
the same time when requiring either a raw loop or an algorithm.
Current (C++17) | Proposed (C++20) |
---|---|
|
|
Another motivating example for using
involves replacing raw loops with algorithms and range
adaptors, but still being able to index the operation in question.
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.
will permit this (see [P0836] §2.1 for how this is beneficial).
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
without needing to rely on IILE.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
in C++20
is a generalisation of
, such that we can apply an n-ary function in place of
the
s above. The following is an example of how
can refine
when tuples
are not necessary.
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
is no longer proposed in C++20, users wanting
functionality will be
required to use something akin to:
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
that are independent
of
are still being worked out, and this should not preclude the usage of zipping ranges
for maximum composability.
4.1.2. Problems with pair
and tuple
requires a value type to store the values of the iterators to the ranges that are
iterated over, and a reference type to access those values via an iterator to the range.
Additionally, the concept "
wants
proxy references to be assignable, so we need a
tuple-like type with
-qualified assignment operators"[cmcstl2].
Both range-v3 and the cmcstl2 implementation above use an exposition-only type derived from
(both) or
(range-v3 only) that permits a
between
(an lvalue reference to the value type) and
(the perceived reference
type). This is deemed to be a hack by both Eric Niebler and Casey Carter; a careful reader might
notice that the above implementation specifies this as
(which has conveniently been
left out of P1035).
Adding a third (and possibly fourth) tuple type that is exposition-only is not ideal: this
requires extensions to both
and
so that they are compatible with
and
, specialisations for all tuple utilities will be required, and overloads to
,
, etc. are also necessary.
Alternatively, we can provide an implicit conversion from
to
, and
ensure that a
exists (à la
), and similarly for the other
reference types.
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
and pair
assignment operators are constexpr
To ensure that the
-ness of
is not accidentally inhibited, this paper also
requests that there be some discussion on making the assignment operators of
and
.
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
constructors
constexpr zip_view ( Rs ... rs ) noexcept ( is_nothrow_move_constructible_v < Rs > && ...) requires ( sizeof ...( Rs ) != 0 );
-
Effects: Initialises
withranges_
.rs ...
4.1.3.2. zip_view
begin
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
end
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 ... >
iterator (exposition-only)
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
members
using iterator_category = see - below ;
-
Effects: If
is satisfied, then( RandomAccessRange < Rs > && ...)
denotesiterator_category
. Otherwise, ifrandom_access_iterator_tag
, then( BidirectionalRange < Rs > && ...)
denotesiterator_category
. Otherwise, ifbidirectional_iterator_tag
, then( ForwardRange < Rs > && ...)
denotesiterator_category
. Otherwise, ifforward_iterator_category
is satisfied, then( InputRange < Rs > && ...)
denotesiterator_category
. Otherwise,input_iterator_tag
denotesiterator_category
.output_iterator_category
using difference_type = see - below ;
-
Effects: If
, thensizeof ...( Rs ) == 0
denotesdifference_type
. Otherwise,int
denotesdifference_type
.common_type_t < iterator_t < conditional_t < Const , const Rs , Rs >> ... >
4.1.3.6. zip_view :: __zipperator
constructors
explicit constexpr __zipperator ( Parent & parent );
-
Effects: Initialises
withparent_
.std :: addressof ( parent )
constexpr __zipperator ( Parent & parent ) requires all_forward ;
-
Effects: Initialises
withparent_
andstd :: addressof ( parent )
with the iterator to the first element in each range (respectively).iterators_
constexpr __zipperator ( Parent & parent , difference_type n ) requires all_random_access ;
-
Effects: Initiaises
withparent_
andstd :: addressof ( parent )
with the nth iterator to each range (respectively). For any rangeiterators_
inr
, a program is undefined ifRs ...
.n > size ( r )
4.1.3.7. zip_view :: __zipperator
element access
constexpr auto operator * (); constexpr auto operator * () const ;
-
Effects: Returns a tuple of references to the elements of each range denoted by
for each iterator in the* 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
advance
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
on each iterator inranges :: advance ( E , n )
, whereiterators_
is a placeholder for the iterator expression.E
constexpr __zipperator & operator -= ( difference_type n ) requires all_random_access ;
-
Effects: Equivalent to
.return * this += - n ;
4.1.3.9. zip_view :: __zipperator
comparisons
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
true
if at least one iterator in
is equivalent to the sentinel of its corresponding range,iterators_ false
otherwise.
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
true
if at least one iterator in
is equivalent to the sentinel.iterators_
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
arithmetic
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
, then returns 0. Otherwise returns the greatest distance between iterators insizeof ...( Rs ) == 0
andx . iterators_
.y . iterators_
4.1.3.11. zip_view < Rs ... >:: __zipperator
iter_move
friend constexpr auto iter_move ( const __zipperator & x ) requires (( Readable < iterator_t < maybe_const < Rs >>> && ...));
-
Effects: Returns a tuple containing expressions equivalent to
applied to each ofiter_move
.x . iterators_
4.1.3.12. zip_view < Rs ... >:: __zipperator
iter_swap
friend constexpr void iter_swap ( const __zipperator & x , const __zipperator & y ) requires ( IndirectlySwappable < iterator_t < maybe_const < Rs >>> && ...);
-
Effects: Equivalent to
applied to each iterator initer_swap
andx . iterators_
.y . iterators_
4.1.3.13. zip_view < Rs ... >
sentinel (exposition-only)
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
denotes a range view adaptor. Let
denote a pack expansion of the
parameter pack
. Then, the expression
is
expression-equivalent to:
-
ifzip_view { rs ...}
is satisfied.( Range < Rs > && ...) && ( is_object_v < Rs > && ...) -
Otherwise
is ill-formed.view :: zip ( rs ...)
4.3. basic_istream_view
4.3.1. Motivation
is an abstraction over a
object, so that it may be used as though
it were an input iterator. It is a great way to populate a container from the get-go, or fill a
container later in its lifetime. This is great, as copy is a standard algorithm that cleanly
communicates that we’re copying something from one range into another. There aren’t any Hidden
SurprisesTM. This is also good when writing generic code, because the generic library
author does not need to care how things are inserted at the end of
, only that they are.
Without
| With
|
---|---|
|
|
The problem with
is that we need to provide a bogus
(or
) every time we want to use it; this acts as the sentinel for
. It is bogus, because the code is equivalent to saying "from the first element of
the istream range until the last element of the istream range", but an
range does not have
a last element. Instead, we denote the end of an istream range to be when the
's failbit is
set. This is otherwise known as the end-of-stream iterator value, but it doesn’t denote a
'past-the-last element' in the same way that call to
does. Because it is the same
for every range, the end-of-stream object may as well be dropped, so that we can write code
that resembles the code below.
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
object fails, insert our
input into the back of
". There is less to read (and type!), and the code is simpler for it.
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
checks that the input type is compatible with the
operator model.
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
constructor
explicit constexpr basic_istream_view ( basic_istream < CharT , Traits >& stream );
-
Effects: Initialises
tostream_
.std :: addressof ( stream )
4.3.2.3. basic_istream_view
begin
constexpr auto begin ();
-
Effects: Equivalent to
* stream_ >> object_ ; return __iterator { * this };
4.3.2.4. basic_istream_view
end
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
constructor
explicit constexpr __iterator ( basic_istream_view < Val >& parent ) noexcept ;
-
Effects: Initialises
withparent_
.std :: addressof ( parent_ )
4.3.2.7. basic_istream_view :: __iterator
next
__iterator & operator ++ (); void operator ++ ( int );
-
Effects: Equivalent to
* parent_ -> stream_ >> parent_ -> object_ ;
4.3.2.8. basic_istream_view :: __iterator
value
Val & operator * () const ;
-
Effects: Equivalent to
return parent_ -> value_ ;
4.3.2.9. basic_istream_view :: __iterator
comparison functions
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
factory
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
, etc., and introducing
, etc. in P1035R1, but the current title is more accurate.
[P1037] introduced
,
,
, and
, which dispatch to templates that correclty identify the associated
types for iterators (formerly known as iterator traits). The current mechanism supports iterators,
but not ranges. For example, in order to extract an range’s value type, we are required to do one of
three things:
- Use the traditional
. As we have had template aliases since C++11, this has probably been abandoned in many projects (not cited).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
asvalue_type_t
, which focuses on iterators, rather than anything definingiter_value_t
alias. We could do this in C++20, but we then riskvalue_type
being different toR :: value_type
(considerR :: iterator :: value_type
andvector < int const >:: value_type
, which arevector < int const >:: iterator :: value_type
andint const
, respectively)[value_type].int - Use
, which what we probably want, and is also a mouthful.iter_value_t < iterator_t < R >>
When discussing extending
so that it can also operate on ranges, Casey Carter had
this to say:
What shouldbe when
iter_value_t < T > is an iterator with value type
T and a range with value type
U ?
V Alternately: What is
?
iter_value_t < array < const int >> is
array < const int >:: value_type , but
const int is
iter_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
as
, which in turn have
been taken from range-v3.
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
and
requirements on the above
associated types. While reviewing a Pull Request to add these to cmcstl2, Casey noted that requiring
and
on the above was not the best approach. The rationale -- taken from a Slack
converation -- is:
Since the "Better Safer Range Access CPOs" work was merged, the definition of
no longer depends on
Range and
iterator_t ; they could be constrained to require
sentinel_t . If that were the case, the
Range 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 on
Range and
iterator_t - but that’s no longer the case.
sentinel_t
P1035 proposes that
and
instead require
. That is,
- 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
, which rangifies the iterator pair
. As an example:
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
will provide slightly different functionality, akin to having a sentinel that
checks if a certain predicate is satisfied.
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
andis_odd_sentinel
were applicable names: ultimately, the nameis_even_sentinel
was chosen because it describes the delimiting condition. An equally valid reason could probably be made foris_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
is an unnecessary requirement. If we look to
and
as prior example, we see that both require invocable objects.
4.5.3.2. take_while_view
constructors
constexpr take_while_view ( R base , Pred pred );
-
Effects: Initialises
withbase_
and initialisesbase
withpred_
.pred
template < ViewableRange O > requires constructible - from - range < R , O > constexpr take_while_view ( O && o , Pred pred );
-
Effects: Initialises
withbase_
and initialisesview :: all ( std :: forward < O > ( o ))
withpred_
.pred
4.5.3.3. take_while_view
conversion
constexpr R base () const ;
-
Returns:
.base_
constexpr const Pred & pred () const ;
-
Returns:
.pred_
4.5.3.4. take_while_view
range begin
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
range end
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
constructor
constexpr explicit __sentinel ( sentinel_t < Base > end , const Pred * pred );
-
Effects: Initialises
withend_
, andend
withpred_
.pred
constexpr __sentinel ( __sentinel <! Const > s ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < Base >> ;
-
Effects Initialises
withend_
ands . end_
withpred_
.s . pred_
4.5.6. take_while_view :: __sentinel
conversion
constexpr sentinel_t < Base > base () const ;
-
Effects: Equivalent to
return end_ ;
4.5.7. take_while_view :: __sentinel
comparisons
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
denotes a range adaptor object. Let
and
be expressions such that type
is
. Then, the expression
is expression-equivalent to:
-
iftake_while_view { E , F }
modelsT
and ifInputRange
is an object, and modelsF
.IndirectUnaryPredicate -
Otherwise
is ill-formed.ranges :: view :: take_while ( E , F )
4.7. drop_view
4.7.1. Motivation
is the complement to
: instead of providing the user with the
first n elements, it provides the user with all but the first n elements.
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
constructor
constexpr drop_view ( R base , D count );
-
Effects: Initialises
withbase_
andbase
withcount_
.count -
Expects:
.0 < count
template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_view ( O && o , D count );
-
Effects: Initialises
withbase_
andview :: all ( std :: forward < O > ( o ))
withcount_
.count -
Expects:
.0 < count
4.7.2.2. drop_view
conversion
constexpr R base () const ;
-
Effects: Equivalent to
.return base_
4.7.2.3. drop_view
range begin
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
for use on subsequent calls.drop_view
4.7.2.4. drop_view
range end
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
size
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
denotes a range adaptor object. Let
and
be expressions such that type
is
. Then, the expression
is expression-equivalent to:
-
ifdrop_view { E , F }
modelsT
andInputRange
is implicitly convertible toF
.iter_difference_t < iterator_t < T >> -
Otherwise
is ill-formed.view :: drop ( E , F )
4.9. drop_while_view
4.9.1. Motivation
The motivation for
is the union of
and
.
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
constructors
constexpr drop_while_view ( R base , Pred pred );
-
Effects: Initialises
withbase_
and initialisesbase
withpred_
.pred
template < ViewableRange O > requires constructible - from - range < R , O > constexpr drop_while_view ( O && o , Pred pred )
-
Effects: Initialises
withbase_
, and intialisesview :: all ( std :: forward < O > ( o ))
withpred_
.pred
4.9.2.2. drop_while_view
conversion
constexpr R base () const ;
-
Returns:
.base_
constexpr Pred pred () const ;
-
Returns:
.pred_
4.9.2.3. drop_while_view
begin
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
concept, the first overload caches the result within theRange
for use on subsequent calls. Without this, applying adrop_while_view
over areverse_view
would have quadratic iteration complexity.drop_view
4.9.2.4. drop_while_view
end
-
Effects: Equivalent to
.return ranges :: end ( base_ );
4.10. view :: drop_while
The name
denotes a range adaptor object. Let
and
be expressions such that
type
is
and
is
. Then, the expression
is expression-equivalent to:
-
ifdrop_while_view { X , Y }
modelsT
, andInputRange
is an object, andY
| and modelsF
.IndirectUnaryPredicate -
Otherwise
is ill-formed.view :: drop_while ( E , F )
4.11. keys
and 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
, given how
frequently such operation is needed we think their addition would make manipulation of associative
containers easier by more clearly expressing the intent.
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
and
operations,
respectively.
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}
-
and the corresponding adaptorkeys_view
iterate over the first elements of a sequence of tuple-like objects of size 2 (tuple of size other than two cannot be considered as representing a key-value pair).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 > ;
-
and the corresponding adaptorvalues_view
iterate over the second elements of a sequence of tuple-like objects of size 2 (tuple of size other than two cannot be considered as representing a key-value pair).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
constructor
constexpr explicit elements_view ( R base );
-
Effects: Initialises
withbase_
.base
4.11.2.2. elements_view
conversion
constexpr R base () const noexcept ;
-
Effects: Equivalent to
.return base_ ;
4.11.2.3. elements_view
range begin
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
range end
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
range size
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 >
iterator
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
constructors
constexpr explicit __iterator ( __parent & parent , iterator_t < __base > current );
-
Effects: Initialises
withparent_
andstd :: addressof ( parent )
withcurrent_
.current
constexpr explicit __iterator ( __iterator <! Const > i ) requires Const && ConvertibleTo < iterator_t < R > , iterator_t < __base >> ;
-
Effects: Initialises
withparent_
andi . parent_
withcurrent_
.i . current_
4.11.3.2. elements_view < R , N >:: __iterator
conversion
constexpr iterator_t < Base > base () const noexcept ;
-
Effects: Equivalent to
.return current_ ;
4.11.3.3. elements_view < R , N >:: __iterator
successor
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
predecessor
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
advance
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
access
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
comparisons
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 >
sentinel
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
constructors
constexpr explicit __sentinel ( __sentinel < __base > end );
-
Effects: Initialises
withend_
.end
constexpr explicit __sentinel ( __sentinel <! Const > i ) requires Const && ConvertibleTo < sentinel_t < R > , sentinel_t < const R >> ;
-
Effects: Initialises
withend_
.i . end
4.11.4.2. elements_view < R , N >:: __sentinel
conversion
constexpr sentinel_t < Base > base () const ;
-
Effects: Equivalent to
.return base_ ;
4.11.4.3. elements_view < R , N >:: __sentinel
comparison
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
andreturn y == x ;
, respectively.return y != x ;
4.11.4.4. elements_view < R , N >:: __sentinel
distance
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
denotes a range adaptor object. Let
be an expression such that
is
. Then, the expression
is expression-equivalent to:
-
ifkeys_view { X }
modelsT
andInputRange
modelsrange_value_t < T >
[range.subrange].pair - like -
Otherwise
is ill-formed.view :: keys ( X )
4.11.6. view :: values
The name
denotes a range adaptor object. Let
be an expression such that
is
. Then, the expression
is expression-equivalent to:
-
ifvalues_view { X }
modelsT
andInputRange
modelsrange_value_t < T >
[range.subrange].pair - like -
Otherwise
is ill-formed.view :: values ( X )
4.11.7. view :: elements
The name
denotes a range adaptor object. Let
be an expression such that
is
. Then, the expression
is expression-equivalent to:
-
ifelements_view < all_view < T > , N > { X }
modelsT
,InputRange
is tuple-like, andrange_value_t < T >
.N < tuple_size_v < range_value_t < T >> -
Otherwise
is ill-formed.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.