Title
ranges::to is over-constrained on the destination type being a range
Status
c++23
Section
[range.utility.conv.to]
Submitter
Barry Revzin

Created on 2022-09-19.00:00:00 last changed 13 months ago

Messages

Date: 2022-11-10.23:33:20

Proposed resolution:

This wording is relative to N4917.

  1. Modify [range.utility.conv.to] as indicated:

    [Drafting note: We need to be careful that this short-circuits, since if C does not satisfy input_range, then range_value_t<C> will be ill-formed.]

    template<class C, input_range R, class... Args> requires (!view<C>)
      constexpr C to(R&& r, Args&&... args);
    

    -1- Returns: An object of type C constructed from the elements of r in the following manner:

    1. (1.1) — If C does not satisfy input_range or convertible_to<range_reference_t<R>, range_value_t<C>> is true:

      1. (1.1.1) — If constructible_from<C, R, Args...> is true:

        C(std::forward<R>(r), std::forward<Args>(args)...)
      2. (1.1.2) — Otherwise, if constructible_from<C, from_range_t, R, Args...> is true:

        C(from_range, std::forward<R>(r), std::forward<Args>(args)...)
      3. (1.1.3) — Otherwise, if

        1. (1.1.3.1) — common_range<R> is true,

        2. (1.1.3.2) — cpp17-input-iterator<iterator_t<R>> is true, and

        3. (1.1.3.3) — constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...> is true:

          C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)
      4. (1.1.4) — Otherwise, if

        1. (1.1.4.1) — constructible_from<C, Args...> is true, and

        2. (1.1.4.2) — container-insertable<C, range_reference_t<R>> is true:

          C c(std::forward<Args>(args)...);
          if constexpr (sized_range<R> && reservable-container<C>)
            c.reserve(ranges::size(r));
          ranges::copy(r, container-inserter<range_reference_t<R>>(c));
          
    2. (1.2) — Otherwise, if input_range<range_reference_t<R>> is true:

      to<C>(r | views::transform([](auto&& elem) {
        return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
      }), std::forward<Args>(args)...);
      
    3. (1.3) — Otherwise, the program is ill-formed.

Date: 2022-11-12.00:00:00

[ 2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP. ]

Date: 2022-09-15.00:00:00

[ 2022-09-28; Reflector poll ]

Set status to Tentatively Ready after five votes in favour during reflector poll. During telecon review we agreed that supporting non-ranges was an intended part of the original design, but was inadvertently broken when adding range_value_t for other reasons.

Date: 2022-09-19.00:00:00

The current wording in [range.utility.conv.to] starts:

If convertible_to<range_reference_t<R>, range_value_t<C>> is true

and then tries to do C(r, args...) and then C(from_range, r, args...). The problem is that C might not be a range — indeed we explicitly removed that requirement from an earlier revision of the paper — which makes this check ill-formed. One example use-case is using ranges::to to convert a range of expected<T, E> into a expected<vector<T>, E>expected isn't any kind of range, but it could support this operation, which is quite useful. Unfortunately, the wording explicitly rejects that. This change happened between R6 and R7 of the paper and seems to have unintentionally rejected this use-case.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2022-11-17 00:42:33adminsetstatus: voting -> wp
2022-11-11 02:39:56adminsetstatus: ready -> voting
2022-11-10 23:33:20adminsetmessages: + msg13005
2022-11-10 23:33:20adminsetstatus: voting -> ready
2022-11-08 03:46:49adminsetstatus: ready -> voting
2022-09-28 16:10:02adminsetmessages: + msg12824
2022-09-28 16:10:02adminsetstatus: new -> ready
2022-09-25 07:28:52adminsetmessages: + msg12812
2022-09-19 00:00:00admincreate