1. Introduction
Currently, many range adaptors require that the user-provided types they store must be copy constructible, which is also required by the assignment wrapper they use,
. This is a consequence of [P1456R1] and [P2325R3] not being integrated together so far. The author believes that this paper is a logical next step from these two papers.
This paper exists, because while writing a paper proposing
(P2474), the author had a desire to make it support move-only types, and needed the tools to specify it. He tried to use the approach in [P2483R0], but has found that approach to not solve the problem correctly.
2. Revision history
2.1. R1
Apply the SG10 recommendation for handling the feature test macro for this change.
Adjust the wording changes to P2474, to align with the wording of a new revision of that paper.
2.2. R0
Initial revision.
3. Design
Similarly to how [P2325R3] turned
into
, this paper proposes to turn
into
. This name is probably not ideal, because it still turns types that happen to be copy constructible into copyable types, but it follows from the prior changes to the wrapper.
The reason why the approach in [P2483R0] is incorrect is that it does not handle types that are copy constructible, but not copy assignable: if a type is copyable, then it works with the wrapper, but if it misses copy assignment, then it does not. The approach taken in this paper solves this issue.
[P2483R0] also suggests future work to relax the requirements on the predicate types stored by standard views. This paper does not perform this relaxation, as the copy constructibility requirement is enshrined in the indirect callable concepts ([indirectcallable.indirectinvocable]). Thus, while this paper modifies the views that currently use
for user provided predicates, it only does so to apply the rename of the exposition-only type to
; it does not change any of the constraints on those views. It does, however, relax the requirements on invocables accepted by the transform family of views, because those are not constrained using the indirect callable concepts.
In effect, the only views whose constraints are changed by this paper are:
-
,single_view -
,transform_view -
, andzip_transform_view -
.adjacent_transform_view
Additionally, this paper proposes to modify the changes in P2474 in this same way. If both this paper and P2474 are accepted, we should make
have the same constraints as
.
3.1. ABI compatibility
The author is not aware of any ABI concerns of this proposal; the question of whether changing constraints on a primary class template has been run by the ABI Review Group, and the responses indicated that constraints are only mangled in for overloadable templates on the major implementations of C++.
3.2. Feature test macro
After asking for SG10’s opinion on the topic on the group’s mailing list, this paper proposes to not introduce a new feature test macro for the proposed feature, and rather increment the value of
- which is consistent with how prior changes of similar nature have been handled.
3.3. Implementation experience
The author has implemented the changes to
and
in CMCSTL2. The changes to the box type were not necessary. CMCSTL2 still contains a
, from before the change to
, but its semiregular box is relaxed to allow for move-only types. Thanks to that, the entire change is replacing "copy" with "move" in the two views mentioned earlier (CMCSTL2 does not include
and
). All the preexisting tests pass with the changes.
4. Wording
4.1. Header < ranges >
synopsis
Modify Header
synopsis [ranges.syn] as follows:
// ... // [range.single], single view template < copy_constructible move_constructible T > requires is_object_v < T > class single_view ; // ... // [range.transform], transform view template < input_range V , copy_constructible move_constructible F > requires view < V > && is_object_v < F > && regular_invocable < F & , range_reference_t < V >> && can - reference < invoke_result_t < F & , range_reference_t < V >>> class transform_view ; // ... // [range.zip.transform], zip transform view template < copy_constructible move_constructible F , input_range ... Views > requires ( view < Views > && ...) && ( sizeof ...( Views ) > 0 ) && is_object_v < F > && regular_invocable < F & , range_reference_t < Views > ... > && can - reference < invoke_result_t < F & , range_reference_t < Views > ... >> class zip_transform_view ; // ... // [range.adjacent.transform], adjacent transform view template < forward_range V , copy_constructible move_constructible F , size_t N > requires see below class adjacent_transform_view ; // ...
4.2. Range factories
4.2.1. Class template single_view
Modify Class template
[range.single.view] as follows:
namespace std :: ranges { template < copy_constructible move_constructible T > requires is_object_v < T > class single_view : public view_interface < single_view < T >> { private : copyable - box movable - box < T > value_ ; // exposition only (see [range. copy move .wrap]) public : single_view () requires default_ initializable = default ; constexpr explicit single_view ( const T & t ) requires copy_constructible < T > ; constexpr explicit single_view ( T && t ); // ...
constexpr explicit single_view ( const T & t ) requires copy_constructible < T > ;
-
Effects: Initializes
withvalue_
.t
4.2.2. Class template iota_view
Do not modify
, which explicitly requires
, and creates copies of the user-provided types as part of its operation.
4.3. Range adaptors
4.3.1. Copyable wrapper
Replace the section Copyable wrapper [range.copy.wrap] with a new section Movable wrapper [range.move.wrap]. The following diff can be applied to the body of [range.copy.wrap] to produce the body of the new [range.move.wrap] section:
-
Many types in this subclause are specified in terms of an exposition-only class template
copyable-boxmovable-box.copyable-boxmovable-box
behaves exactly like< T >
with the following differences:optional < T > -
copyable-boxmovable-box
constrains its type parameter< T >
withT
.copy_constructible move_constructible < T > && is_object_v < T > -
The default constructor of
copyable-boxmovable-box
is equivalent to:< T > constexpr copyable - box movable - box () noexcept ( is_nothrow_default_constructible_v < T > ) requires default_initializable < T > : copyable - box movable - box { in_place } {} -
If
is not modeled, the copy assignment operator is equivalent to:copyable < T > constexpr copyable - box movable - box & operator = ( const copyable - box movable - box & that ) noexcept ( is_nothrow_copy_constructible_v < T > ) requires copy_constructible < T > { if ( this != addressof ( that )) { if ( that ) emplace ( * that ); else reset (); } return * this ; } -
If
is not modeled, the move assignment operator is equivalent to:movable < T > constexpr copyable - box movable - box & operator = ( copyable - box movable - box && that ) noexcept ( is_nothrow_move_constructible_v < T > ) { if ( this != addressof ( that )) { if ( that ) emplace ( std :: move ( * that )); else reset (); } return * this ; }
-
-
Recommended practice:
copyable-box
should store only a< T >
if eitherT
modelsT
orcopyable
isis_nothrow_move_constructible_v < T > && is_nothrow_copy_constructible_ v < T > true
.-
If
iscopy_constructible < T > true
, movable-box
should store only a< T >
if eitherT
modelsT
, orcopyable
isis_nothrow_move_constructible_v < T > && is_nothrow_copy_constructible_v < T > true
. -
Otherwise, movable-box
should store only a< T >
if eitherT
modelsT
ormovable
isis_nothrow_move_constructible_v < T > true
.
-
4.3.2. Class template filter_view
Modify Class template
[range.filter.view] as follows:
namespace std :: ranges { template < input_range V , indirect_unary_predicate < iterator_t < V >> Pred > requires view < V > && is_object_v < Pred > class filter_view : public view_interface < filter_view < V , Pred >> { private : V base_ = V (); // exposition only copyable - box movable - box < Pred > pred_ ; // exposition only // ...
4.3.3. Class template transform_view
Modify Class template
[range.transform.view] as follows:
namespace std :: ranges { template < input_range V , copy_constructible move_constructible F > requires view < V > && is_object_v < F > && regular_invocable < F & , range_reference_t < V >> && can - reference < invoke_result_t < F & , range_reference_t < V >>> class transform_view : public view_interface < transform_view < V , F >> { private : // [range.transform.iterator], class template transform_view::iterator template < bool > struct iterator ; // exposition only // [range.transform.sentinel], class template transform_view::sentinel template < bool > struct sentinel ; // exposition only V base_ = V (); // exposition only copyable - box movable - box < F > fun_ ; // exposition only // ...
4.3.4. Class template take_while_view
Modify Class template
[range.take.while.view] as follows:
namespace std :: ranges { template < view V , class Pred > requires input_range < V > && is_object_v < Pred > && indirect_unary_predicate < const Pred , iterator_t < V >> class take_while_view : public view_interface < take_while_view < V , Pred >> { // [range.take.while.sentinel], class template take_while_view::sentinel template < bool > class sentinel ; // exposition only V base_ = V (); // exposition only copyable - box movable - box < Pred > pred_ ; // exposition only // ...
4.3.5. Class template drop_while_view
namespace std :: ranges { template < view V , class Pred > requires input_range < V > && is_object_v < Pred > && indirect_unary_predicate < const Pred , iterator_t < V >> class drop_while_view : public view_interface < drop_while_view < V , Pred >> { public : drop_while_view () requires default_initializable < V > && default_initializable < Pred > = default ; constexpr drop_while_view ( V base , Pred pred ); constexpr V base () const & requires copy_constructible < V > { return base_ ; } constexpr V base () && { return std :: move ( base_ ); } constexpr const Pred & pred () const ; constexpr auto begin (); constexpr auto end () { return ranges :: end ( base_ ); } private : V base_ = V (); // exposition only copyable - box movable - box < Pred > pred_ ; // exposition only // ...
4.3.6. Class template zip_transform_view
Modify Class template
[range.zip.transform.view] as follows:
namespace std :: ranges { template < copy_constructible move_constructible F , input_range ... Views > requires ( view < Views > && ...) && ( sizeof ...( Views ) > 0 ) && is_object_v < F > && regular_invocable < F & , range_reference_t < Views > ... > && can - reference < invoke_result_t < F & , range_reference_t < Views > ... >> class zip_transform_view : public view_interface < zip_transform_view < F , Views ... >> { copyable - box movable - box < F > fun_ ; // exposition only // ...
4.3.7. Class template adjacent_transform_view
Modify Class template
[range.adjacent.transform.view] as follows:
namespace std :: ranges { template < forward_range V , copy_constructible move_constructible F , size_t N > requires view < V > && ( N > 0 ) && is_object_v < F > && regular_invocable < F & , REPEAT ( range_reference_t < V > , N )... > && can - reference < invoke_result_t < F & , REPEAT ( range_reference_t < V > , N )... >> class adjacent_transform_view : public view_interface < adjacent_transform_view < V , F , N >> { copyable - box movable - box < F > fun_ ; // exposition only // ...
4.4. Feature test macro
Bump the Ranges feature-test macro in 17.3.2 [version.syn], Header
synopsis, with the value selected by the editor to reflect the date of adoption of this paper:
#define __cpp_lib_ranges 202110L 20XXXXL
5. Modifications to P2474
Note: these changes should be applied to the Working Draft if both this paper and P2474 are approved.
Modify Header
synopsis [ranges.syn] as follows:
namespace std :: ranges { // ... // [range.repeat], repeat view template < copy_constructible move_constructible W , semiregular Bound = unreachable_sentinel_t > requires ( is_object_v < W > && same_as < W , remove_cv_t < W >> && ( is - integer - like < Bound > || same_as < Bound , unreachable_sentinel_t > )) class repeat_view ; // ... }
Modify Class template
[range.repeat.view] as follows:
namespace std :: ranges { template < copy_constructible move_constructible W , semiregular Bound = unreachable_sentinel_t > requires ( is_object_v < W > && same_as < W , remove_cv_t < W >> && ( 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 ; copyable - box movable - box < W > value_ = W (); // exposition only (see [range. copy move .wrap]) Bound bound_ = Bound (); // exposition only public : repeat_view () requires default_initializable < W > = default ; constexpr explicit repeat_view ( const W & value , Bound bound = Bound ()) requires copy_constructible < W > ; // ...
constexpr explicit repeat_view ( const W & value , Bound bound = Bound ()) requires copy_constructible < W > ;
-
Effects: Initializes
withvalue_
andvalue
withbound_
.bound
6. Acknowledgements
Thanks to Christopher Di Bella, Corentin Jabot, and Casey Carter for a review and comments of this paper. Thanks to Hui Xie for authoring [P2483R0].