explicit
Section: 25.7.26.2 [range.zip.transform.view], 25.7.28.2 [range.adjacent.transform.view], 25.7.29.2 [range.chunk.view.input], 25.7.29.6 [range.chunk.view.fwd], 25.7.30.2 [range.slide.view], 25.7.31.2 [range.chunk.by.view] Status: NAD Submitter: Hewill Kang Opened: 2022-06-10 Last modified: 2024-06-24
Priority: 4
View all issues with NAD status.
Discussion:
All C++20 range adaptors' non-single-argument constructors are not declared as explicit
, which makes
the following initialization well-formed:
std::vector v{42}; std::ranges::take_view r1 = {v, 1}; std::ranges::filter_view r2 = {v, [](int) { return true; }};
However, the non-single-argument constructors of C++23 range adaptors, except for join_with_view
,
are all explicit
, which makes us no longer able to
std::ranges::chunk_view r1 = {v, 1}; // ill-formed std::ranges::chunk_by_view r2 = {v, [](int, int) { return true; }}; // ill-formed
This seems unnecessary since I don't see the observable benefit of preventing this. In the standard,
non-single-argument constructors are rarely specified as explicit
unless it is really undesirable,
I think the above initialization is what the user expects since it's clearly intentional, and I don't see
any good reason to reject it from C++23.
[2022-06-11; Daniel comments]
Another possible candidate could be 25.7.11.3 [range.take.while.sentinel]'s
constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
[2022-06-21; Reflector poll]
Set priority to 4 after reflector poll. Send to LEWG.
[2023-01-24; LEWG in Kona]
Use alternative approach in P2711 instead.
[2024-06-24 Status changed: Tentatively NAD → NAD.]
P2711R1 was approved in February 2023, confirming that these constructors should be explicit.
Proposed resolution:
This wording is relative to N4910.
Modify 25.7.26.2 [range.zip.transform.view] as indicated:
namespace std::ranges { template<copy_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<F> fun_; // exposition only zip_view<Views...> zip_; // exposition only […] public: zip_transform_view() = default; constexprexplicitzip_transform_view(F fun, Views... views); […] }; […] }constexprexplicitzip_transform_view(F fun, Views... views);-1- Effects: Initializes
fun_
withstd::move(fun)
andzip_
withstd::move(views)...
.
Modify 25.7.28.2 [range.adjacent.transform.view] as indicated:
namespace std::ranges { template<forward_range V, copy_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<F> fun_; // exposition only adjacent_view<V, N> inner_; // exposition only […] public: adjacent_transform_view() = default; constexprexplicitadjacent_transform_view(V base, F fun); […] }; […] }constexprexplicitadjacent_transform_view(V base, F fun);-1- Effects: Initializes
fun_
withstd::move(fun)
andinner_
withstd::move(base)
.
Modify 25.7.29.2 [range.chunk.view.input] as indicated:
namespace std::ranges { […] template<view V> requires input_range<V> class chunk_view : public view_interface<chunk_view<V>> { V base_ = V(); // exposition only range_difference_t<V> n_ = 0; // exposition only […] public: chunk_view() requires default_initializable<V> = default; constexprexplicitchunk_view(V base, range_difference_t<V> n); […] }; […] }constexprexplicitchunk_view(V base, range_difference_t<V> n);-1- Preconditions:
-2- Effects: Initializesn > 0
istrue
.base_
withstd::move(base)
andn_
withn
.
Modify 25.7.29.6 [range.chunk.view.fwd] as indicated:
namespace std::ranges { template<view V> requires forward_range<V> class chunk_view<V> : public view_interface<chunk_view<V>> { V base_ = V(); // exposition only range_difference_t<V> n_ = 0; // exposition only […] public: chunk_view() requires default_initializable<V> = default; constexprexplicitchunk_view(V base, range_difference_t<V> n); […] }; }constexprexplicitchunk_view(V base, range_difference_t<V> n);-1- Preconditions:
-2- Effects: Initializesn > 0
istrue
.base_
withstd::move(base)
andn_
withn
.
Modify 25.7.30.2 [range.slide.view] as indicated:
namespace std::ranges { […] template<forward_range V> requires view<V> class slide_view : public view_interface<slide_view<V>> { V base_ = V(); // exposition only range_difference_t<V> n_ = 0; // exposition only […] public: slide_view() requires default_initializable<V> = default; constexprexplicitslide_view(V base, range_difference_t<V> n); […] }; […] }constexprexplicitslide_view(V base, range_difference_t<V> n);-1- Effects: Initializes
base_
withstd::move(base)
andn_
withn
.
Modify 25.7.31.2 [range.chunk.by.view] as indicated:
namespace std::ranges { template<forward_range V, indirect_binary_predicate<iterator_t<V>, iterator_t<V>> Pred> requires view<V> && is_object_v<Pred> class chunk_by_view : public view_interface<chunk_by_view<V, Pred>> { V base_ = V(); // exposition only copyable-box<Pred> pred_ = Pred(); // exposition only […] public: chunk_by_view() requires default_initializable<V> && default_initializable<Pred> = default; constexprexplicitchunk_by_view(V base, Pred pred); […] }; […] }constexprexplicitchunk_by_view(V base, Pred pred);-1- Effects: Initializes
base_
withstd::move(base)
andpred_
withstd::move(pred)
.