Issue 3714: Non-single-argument constructors for range adaptors should not be explicit (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD status.
3714. Non-single-argument constructors for range adaptors should not be 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, { 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 && ...) && (sizeof...(Views) > 0) && is_object_v &&
regular_invocable<F&, range_reference_t...> &&
_can-reference_<invoke_result_t<F&, range_reference_t<Views>...>>class zip_transform_view : public view_interface<zip_transform_view<F, Views...>> {
copyable-box fun; // exposition only
zip_view<Views...> zip; // exposition only
[…]
public:
zip_transform_view() = default;constexpr
explicitzip_transform_view(F fun, Views... views);
[…]
};
[…]
}constexpr
explicitzip_transform_view(F fun, Views... views);-1- Effects: Initializes
_fun_withstd::move(fun)and_zip_withstd::move(views).... 2. 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 && (N > 0) && is_object_v &&
regular_invocable<F&, _REPEAT_(range_reference_t, N)...> &&
can-reference<invoke_result_t<F&, _REPEAT_(range_reference_t, N)...>>
class adjacent_transform_view : public view_interface<adjacent_transform_view<V, F, N>> {
copyable-box fun; // exposition only
adjacent_view<V, N> inner; // exposition only
[…]
public:
adjacent_transform_view() = default;
constexprexplicitadjacent_transform_view(V base, F fun);
[…]
};
[…]
}constexpr
explicitadjacent_transform_view(V base, F fun);-1- Effects: Initializes
_fun_withstd::move(fun)and_inner_withstd::move(base). 3. Modify 25.7.29.2 [range.chunk.view.input] as indicated:
namespace std::ranges {
[…]
template
requires input_range
class chunk_view : public view_interface<chunk_view> {
V base = V(); // exposition only
range_difference_t n = 0; // exposition only
[…]
public:
chunk_view() requires default_initializable = default;
constexprexplicitchunk_view(V base, range_difference_t n);
[…]
};
[…]
}constexpr
explicitchunk_view(V base, range_difference_t n);-1- Preconditions:
n > 0istrue.-2- Effects: Initializes
_base_withstd::move(base)and_n_withn. 4. Modify 25.7.29.6 [range.chunk.view.fwd] as indicated:
namespace std::ranges {
template
requires forward_range
class chunk_view : public view_interface<chunk_view> {
V base = V(); // exposition only
range_difference_t n = 0; // exposition only
[…]
public:
chunk_view() requires default_initializable = default;
constexprexplicitchunk_view(V base, range_difference_t n);
[…]
};
}constexpr
explicitchunk_view(V base, range_difference_t n);-1- Preconditions:
n > 0istrue.-2- Effects: Initializes
_base_withstd::move(base)and_n_withn. 5. Modify 25.7.30.2 [range.slide.view] as indicated:
namespace std::ranges {
[…]
template
requires view
class slide_view : public view_interface<slide_view> {
V base = V(); // exposition only
range_difference_t n = 0; // exposition only
[…]
public:
slide_view() requires default_initializable = default;
constexprexplicitslide_view(V base, range_difference_t n);
[…]
};
[…]
}constexpr
explicitslide_view(V base, range_difference_t n);-1- Effects: Initializes
_base_withstd::move(base)and_n_withn. 6. Modify 25.7.31.2 [range.chunk.by.view] as indicated:
namespace std::ranges {
template<forward_range V, indirect_binary_predicate<iterator_t, iterator_t> Pred>
requires view && is_object_v
class chunk_by_view : public view_interface<chunk_by_view<V, Pred>> {
V base = V(); // exposition only
copyable-box pred = Pred(); // exposition only
[…]
public:
chunk_by_view() requires default_initializable && default_initializable = default;
constexprexplicitchunk_by_view(V base, Pred pred);
[…]
};
[…]
}constexpr
explicitchunk_by_view(V base, Pred pred);-1- Effects: Initializes
_base_withstd::move(base)and_pred_withstd::move(pred).