**Section:** 26.5.7.2 [range.utility.conv.to] **Status:** C++23
**Submitter:** Barry Revzin **Opened:** 2022-09-19 **Last modified:** 2023-11-22 15:47:43 UTC

**Priority: **Not Prioritized

**View other** active issues in [range.utility.conv.to].

**View all other** issues in [range.utility.conv.to].

**View all issues with** C++23 status.

**Discussion:**

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

If

convertible_to<range_reference_t<R>, range_value_t<C>>istrue

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.

*[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.

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

**Proposed resolution:**

This wording is relative to N4917.

Modify 26.5.7.2 [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) — If

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

`constructible_from<C, R, Args...>`is`true`:(1.1.2) — Otherwise, if

`constructible_from<C, from_range_t, R, Args...>`is`true`:(1.1.3) — Otherwise, if

(1.1.3.1) —

`common_range<R>`is`true`,(1.1.3.2) —

is*cpp17-input-iterator*<iterator_t<R>>`true`, and(1.1.3.3) —

`constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>`is`true`:(1.1.4) — Otherwise, if

(1.1.4.1) —

`constructible_from<C, Args...>`is`true`, and(1.1.4.2) —

is*container-insertable*<C, range_reference_t<R>>`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));(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)...);

(1.3) — Otherwise, the program is ill-formed.

`C(std::forward<R>(r), std::forward<Args>(args)...)``C(from_range, std::forward<R>(r), std::forward<Args>(args)...)``C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)`