This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.

### 3714. Non-single-argument constructors for range adaptors should not be `explicit`

Section: 26.7.24.2 [range.zip.transform.view] Status: LEWG Submitter: Hewill Kang Opened: 2022-06-10 Last modified: 2022-06-21

Priority: 4

View all issues with LEWG 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 26.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.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.24.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;

   > >         constexpr explicit zip_transform_view(F fun, Views... views);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr explicit zip_transform_view(F fun, Views... views);

   > > -1- Effects: Initializes `fun_` with `std::move(fun)` and `zip_` with `std::move(views)...`.

2. Modify 26.7.26.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;
   > >         constexpr explicit adjacent_transform_view(V base, F fun);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr explicit adjacent_transform_view(V base, F fun);

   > > -1- Effects: Initializes `fun_` with `std::move(fun)` and `inner_` with `std::move(base)`.

3. Modify 26.7.27.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;
   > >         constexpr explicit chunk_view(V base, range_difference_t<V> n);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr explicit chunk_view(V base, range_difference_t<V> n);

   > > -1- Preconditions: `n > 0` is `true`.

   > > -2- Effects: Initializes `base_` with `std::move(base)` and `n_` with `n`.

4. Modify 26.7.27.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;
   > >         constexpr explicit chunk_view(V base, range_difference_t<V> n);
   > >         […]
   > >       };
   > >     }

   >     constexpr explicit chunk_view(V base, range_difference_t<V> n);

   > > -1- Preconditions: `n > 0` is `true`.

   > > -2- Effects: Initializes `base_` with `std::move(base)` and `n_` with `n`.

5. Modify 26.7.28.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;
   > >         constexpr explicit slide_view(V base, range_difference_t<V> n);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr explicit slide_view(V base, range_difference_t<V> n);

   > > -1- Effects: Initializes `base_` with `std::move(base)` and `n_` with `n`.

6. Modify 26.7.29.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;
   > >         constexpr explicit chunk_by_view(V base, Pred pred);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr explicit chunk_by_view(V base, Pred pred);

   > > -1- Effects: Initializes `base_` with `std::move(base)` and `pred_` with `std::move(pred)`.