1. Introduction
This paper proposes two new range factories:
-
, which is an infinite range that produces the same value repeatedly; andviews :: repeat -
, which is a finite range of a specified size that produces the same value repeatedly.views :: repeat_n
2. Changelog
2.1. R0
Initial revision.
3. Motivation
// TODO
4. Design
This paper proposes the following additions to the Ranges library:
-
A new view type,
, a view which repeatedly produces the same value, based on the design ofrepeat_view
.iota_view -
A new customization point object,
, which returns aviews :: repeat
, based on the design ofrepeat_view
.views :: iota -
An extension for the semantics of
andviews :: take
, to make them return boundedviews :: drop
when passed arepeat_view
.repeat_view
[P2214R1] suggests that
and
could be implemented in terms of
and
, however it also points out that the implementation of
in range-v3 contains a data race. To avoid this issue, this paper defines
and
similarly to how
is defined, which avoids the data race in
. (The data race manifests in the implementation of
filling its value cache inside a
function. There’s a number of ways to resolve this issue, but the author would rather not make solving it a prerequisite for
.)
4.1. repeat_view
is a class template that’s parametrized on the type of the value it produces, and an additional parameter which controls whether it is bounded or not. This view is a common range and a sized range when bounded. It is always a random access range.
4.2. views :: repeat
is a customization point object which returns an unbounded
when passed just a value, and a bounded
when passed a value and a bound.
4.3. Move-only types and borrowed range
Currently, there is no support for move-only types to be stored inside views; as is, this impacts
(which requires the value type to be copyable), and the
family of adaptors (which requires the transform function to be copyable). The author proposes that this paper follow the requirements in the other adaptors, but also separately proposes to relax these requirements for both the existing adaptors and for the proposed
in P2494.
This paper also proposes that iterators to
s do not store copies of the values. There’s several reasons for this. For one, unlike the case of
, all the iterators to a
always refer to the same value; copying the values all over to produce the same one from every iterator does not seem ideal. For another, if the iterators stored copies, the relaxation of the concept requirements mentioned above would not be possible. Finally, there is a precedent for this behavior:
in Range-v3 does not copy the values into iterators, even though it still requires that the values be copyable.
This, however, means that
is not a borrowed range. This is a tradeoff, but the author believes that it is a worthy one.
4.4. Category
is always random access, because the variables being incremented while iterating are always integers or integer-like classes.
4.5. Other properties
is sized when a bound other than
is provided.
is common when it is sized.
4.6. views :: take
and views :: drop
changes
and
are specialized for a sized
, in which case they return an
back. They require that the
be sized, because the bound type for
can be an arbitrary type, so it is possible to have an
that is random access and finite, but for which there exists no way to obtain the size. In such a case, if we attempted to blindly increment the iterators by the value of the second argument of either
or
, we could inadvertently reach an iterator that seems valid, but is in fact past the end of the range.
For
, this situation is much clearer: the value is never modified, and the bound is either
, or an integer like type. In both cases we know that we can reuse the value, and set the bound to the appropriate value. Therefore, this paper proposes to extend the specializations of
and
for
, without requiring that it must be sized.
5. Implementation experience
Range-v3 implements both the bounded and the unbounded variants of
, but exposes them as separate functions and types. The author believes that this facility should instead follow the design of
. The two versions of
in Range-v3 differ only by the existence of the size field in the view itself, by the existence of the
function, and by the variant of the
base class they inherit from.
6. Wording
6.1. Addition to < ranges >
Add the following to 24.2 [range.syn], Header
synopsis:
namespace std :: ranges { // ... namespace views { inline constexpr unspecified iota = unspecified ; } // [range.repeat], repeat view template < copy_constructible W , semiregular Bound = unreachable_sentinel_t > class repeat_view ; namespace views { inline constexpr unspecified repeat = unspecified ; } // [range.istream], istream view // ... }
6.2. Repeat view [range.repeat]
Add a new section, Repeat view [range.repeat], after Iota view [range.iota].
6.2.1. Overview [range.repeat.overview]
-
generates a sequence of elements by repeatedly producing the same value.repeat_view -
The name
denotes a customization point object ([customization.point.object]). Given subexpressionsviews :: repeat
andE
, the expressionsF
andviews :: repeat ( E )
are expression-equivalent toviews :: repeat ( E , F )
andrepeat_view ( E )
, respectively.repeat_view ( E , F ) -
[ Example:
for ( int i : views :: repeat ( 17 , 4 )) cout << i << ' ' ; // prints: 17 17 17 17
6.2.2. Class template repeat_view
[range.repeat.view]
namespace std :: ranges { template < copy_constructible W , semiregular Bound = unreachable_sentinel_t > requires is - integer - like < Bound > || same_as < Bound , unreachable_sentinel_t > class repeat_view : public view_interface < repeat_view < W , Bound >> { private : // [range.repeat.iterator], class range_view::iterator struct iterator ; movable - box < W > value_ = W (); // exposition only, see [range.move.wrap] Bound bound_ = Bound (); // exposition only public : repeat_view () requires default_initializable < W > = default ; template < semiregular Bnd > constexpr explicit repeat_view ( const repeat_view < V , Bnd > & r , type_identity_t < Bound > bound = Bound ()); template < semiregular Bnd > constexpr explicit repeat_view ( repeat_view < V , Bnd > && r , type_identity_t < Bound > bound = Bound ()); constexpr explicit repeat_view ( const W & value , Bound bound = Bound ()); constexpr explicit repeat_view ( W && value , Bound bound = Bound ()); template < class ... Args > requires constructible_from < W , Args ... > constexpr explicit repeat_view ( in_place_t , Args && ... args ); template < class ... Args > requires constructible_from < W , Args ... > constexpr repeat_view ( in_place_t , Bound bound , Args && ... args ); constexpr iterator begin () const ; constexpr iterator end () const requires ! same_as < Bound , unreachable_sentinel_t > ; constexpr unreachable_sentinel_t end () const ; constexpr auto size () requires ! same_as < Bound , unreachable_sentinel_t > ; }; }
template < semiregular Bnd > constexpr explicit repeat_view ( const repeat_view < V , Bnd > & r , type_identity_t < Bound > bound = Bound ());
-
Effects: Initializes
withvalue_
andr . value_
withbound_
.bound
template < semiregular Bnd > constexpr explicit repeat_view ( repeat_view < V , Bnd > && r , type_identity_t < Bound > bound = Bound ());
-
Effects: Initializes
withvalue_
andstd :: move ( r . value_ )
withbound_
.bound
constexpr explicit repeat_view ( const W & value , Bound bound = Bound ());
-
Effects: Initializes
withvalue_
andvalue
withbound_
.bound
constexpr explicit repeat_view ( W && value , Bound bound = Bound ());
-
Effects: Initializes
withvalue_
andstd :: move ( value )
withbound_
.bound
template < typename ... Args > requires constructible_from < W , Args ... > constexpr explicit repeat_view ( in_place_t , Args && ... args );
-
Effects: Initializes
withvalue_
.std :: forward < Args > ( args )...
template < typename ... Args > requires constructible_from < W , Args ... > constexpr repeat_view ( in_place_t , Bound bound , Args && ... args );
-
Effects: Initializes
withvalue_
andstd :: forward < Args > ( args )...
withbound_
.bound
constexpr iterator begin () const ;
-
Effects: Equivalent to
return iterator { & value_ };
constexpr iterator end () const requires ! same_as < Bound , unreachable_sentinel_t > ;
-
Effects: Equivalent to
return iterator { & value , bound_ };
constexpr unreachable_sentinel_t end () const ;
-
Effects: Equivalent to
return unreachable_sentinel ;
constexpr auto size () requires ! same_as < Bound , unreachable_sentinel_t > ;
-
Effects: Equivalent to
return bound_ ;
6.2.3. Class template repeat_view :: iterator
[range.repeat.iterator]
namespace std :: ranges { template < copyable W , semireular Bound = unreachable_sentinel_t > requires is - integer - like < Bound > || same_as < Bound , unreachable_sentinel_t > class repeat_view < W , Bound >:: iterator { private : using index_type = // exposition only conditional_t < same_as < Bound , unreachable_sentinel_t > , ptrdiff_t , Bound > ; const W * value_ ; // exposition only index_type current_ = index_type (); // exposition only public : using iterator_concept = random_access_iterator_tag ; using iterator_catetogy = random_access_iterator_tag ; using value_type = W ; using reference = const W & ; using difference = see below ; iterator () requires default_initializable < W > = default ; constexpr explicit iterator ( const W * value , index_type b = index_type ()); constexpr const W & operator * () const noexcept ; constexpr iterator & operator ++ (); constexpr iterator operator ++ ( int ); constexpr iterator & operator -- (); constexpr iterator operator -- ( int ); constexpr iterator & operator += ( difference_type n ); constexpr iterator & operator -= ( difference_type n ); constexpr const W & operator []( difference_type n ) const noexcept ; friend constexpr bool operator == ( const iterator & x , const iterator & y ); friend constexpr bool operator < ( const iterator & x , const iterator & y ); friend constexpr bool operator > ( const iterator & x , const iterator & y ); friend constexpr bool operator <= ( const iterator & x , const iterator & y ); friend constexpr bool operator >= ( const iterator & x , const iterator & y ); friend constexpr auto operator <=> ( const iterator & x , const iterator & y ); friend constexpr iterator operator + ( iterator i , difference_type n ); friend constexpr iterator operator + ( difference_type n , iterator i ); friend constexpr iterator operator - ( iterator i , difference_type n ); friend constexpr difference_type operator - ( const iterator & x , const iterator & y ); }; }
constexpr explicit iterator ( const W * value , index_type b = index_type ());
-
Effects: Initializes
withvalue_
andvalue
withbound_
.b
constexpr const W & operator * () const noexcept ;
-
Effects: Equivalent to
return * value_ ;
constexpr iterator & operator ++ ();
-
Effects: Equivalent to:
++ current_ ; return * this ;
constexpr iterator operator ++ ( int );
-
Effects: Equivalent to:
auto tmp = * this ; ++* this ; return tmp ;
constexpr iterator & operator -- ();
-
Effects: Equivalent to:
-- current_ ; return * this ;
constexpr iterator operator -- ( int );
-
Effects: Equivalent to:
auto tmp = * this ; --* this ; return tmp ;
constexpr iterator & operator += ( difference_type n );
-
Effects: Equivalent to:
current_ += n ; return * this ;
constexpr iterator & operator -= ( difference_type n );
-
Effects: Equivalent to:
current_ -= n ; return * this ;
constexpr const W & operator []( difference_type n ) const noexcept ;
-
Effects: Equivalent to
return * value_ ;
friend constexpr bool operator == ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current_ == y . current_ ;
friend constexpr bool operator < ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current_ < y . current_ ;
friend constexpr bool operator > ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current_ > y . current_ ;
friend constexpr bool operator <= ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current_ <= y . current_ ;
friend constexpr bool operator >= ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current_ >= y . current_ ;
friend constexpr auto operator <=> ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
x . current <=> y . current_ ;
friend constexpr iterator operator + ( iterator i , difference_type n ); friend constexpr iterator operator + ( difference_type n , iterator i );
-
Effects: Equivalent to
return iterator { i . value_ , i . current_ + n };
friend constexpr iterator operator - ( iterator i , difference_type n );
-
Effects: Equivalent to
return iterator { i . value_ , i . current_ - n };
friend constexpr difference_type operator - ( const iterator & x , const iterator & y );
-
Effects: Equivalent to
return x . current_ - y . current_ ;
6.3. Take view [range.take]
6.3.1. Overview [range.take.overview]
Modify Overview [range.take.overview] paragraph 2 as follows:
2. The name
denotes a range adaptor object ([range.adaptor.object]).
Let
and
be expressions, let
be
, and let
be
.
If
does not model
,
is ill-formed.
Otherwise, the expression
is expression-equivalent to:
-
If
is a specialization ofT
([range.empty.view]), thenranges :: empty_view
, except that the evaluations of(( void ) F , decay - copy ( E ))
andE
are indeterminately sequenced.F -
Otherwise, if
modelsT
andrandom_access_range
and is a specialization ofsized_range
([views.span]),span
([string.view]), orbasic_string_view
([range.subrange]), thenranges :: subrange
, except thatU ( ranges :: begin ( E ), ranges :: begin ( E ) + std :: min < D > ( ranges :: distance ( E ), F ))
is evaluated only once, whereE
is a type determined as follows:U -
if
is a specialization ofT
, thenspan
isU
;span < typename T :: element_type > -
otherwise, if
is a specialization ofT
, thenbasic_string_view
isU
;T -
otherwise,
is a specialization ofT
, andranges :: subrange
isU
;ranges :: subrange < iterator_t < T >>
-
-
Otherwise, if
is a specialization ofT
([range.iota.view]) that modelsranges :: iota_view
andrandom_access_range
, thensized_range
, except thatranges :: iota_view ( * ranges :: begin ( E ), * ( ranges :: begin ( E ) + std :: min < D > ( ranges :: distance ( E ), F )))
is evaluated only once;E
-
Otherwise, if
is a specialization ofT
([range.repeat.view]):ranges :: repeat_view -
if
modelsT
, thensized_range
, except thatranges :: repeat_view < range_value_t < T > , D > ( std :: forward < decltype ( E ) > ( E ), std :: min < D > ( ranges :: distance ( E ), F ))
is evaluated only once;E -
otherwise,
;ranges :: repeat_view < range_value_t < T > , D > ( E , F )
-
-
Otherwise,
.ranges :: take_view ( E , F )
6.4. Drop view [range.drop]
6.4.1. Overview [range.drop.overview]
Modify Overview [range.drop.overview] paragraph 2 as follows:
2. The name
denotes a range adaptor object ([range.adaptor.object]).
Let
and
be expressions, let
be
, and let
be
.
If
does not model
,
is ill-formed.
Otherwise, the expression
is expression-equivalent to:
-
If
is a specialization ofT
([range.empty.view]), thenranges :: empty_view
, except that the evaluations of(( void ) F , decay - copy ( E ))
andE
are indeterminately sequenced.F -
Otherwise, if
modelsT
andrandom_access_range
and issized_range -
a specialization of
([views.span]),span -
a specialization of
([string.view]),basic_string_view -
a specialization of
([range.iota.view]), orranges :: iota_view -
a specialization of
([range.subrange]) whereranges :: subrange
isT :: StoreSize false
,
then
, except thatU ( ranges :: begin ( E ) + std :: min < D > ( ranges :: distance ( E ), F ), ranges :: end ( E ))
is evaluated only once, whereE
isU
ifspan < typename T :: element_type >
is a specialization ofT
andspan
otherwise.T -
-
Otherwise, if
is a specialization ofT
([range.subrange]) that modelsranges :: subrange
andrandom_access_range
, thensized_range
, except thatT ( ranges :: begin ( E ) + std :: min < D > ( ranges :: distance ( E ), F ), ranges :: end ( E ), to - unsigned - like ( ranges :: distance ( E ) - std :: min < D > ( ranges :: distance ( E ), F )))
andE
are each evaluated only once.F
-
Otherwise, if
is a specialization ofT
([range.repeat.view]):ranges :: repeat_view -
if
modelsT
, thensized_range
;ranges :: repeat_view < range_value_t < T > , D > ( std :: forward < decltype ( E ) > ( E ), ranges :: distance ( E ) - std :: min < D > ( ranges :: distance ( E ), F )) -
otherwise,
.E
-
-
Otherwise,
.ranges :: drop_view ( E , F )