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

3983. ranges::to adaptors are underconstrained

Section: 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Hewill Kang Opened: 2023-08-23 Last modified: 2023-11-03

Priority: 3

View all issues with New status.

Discussion:

The ranges::to adaptor returns a range adaptor closure object that stores a copy of the decayed parameter pack.

However, the function signature does not constrain the type of arguments, which leads to hard errors within the function body when they cannot be copied (demo):

#include <ranges>
#include <vector>

struct NonMovable {
  NonMovable() = default;
  NonMovable(NonMovable&&) = delete;
};

int main() {
  auto r = std::ranges::to<std::vector>(NonMovable{}); // hard error in MSVC-STL and libc++
}

Previous resolution [SUPERSEDED]:

This wording is relative to N4958.

  1. Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:

    #include <compare>              // see [compare.syn]
    #include <initializer_list>     // see [initializer.list.syn]
    #include <iterator>             // see [iterator.synopsis]
    
    namespace std::ranges {
      […]
      // 25.5.7 [range.utility.conv], range conversions
      template<class C, input_range R, class... Args> requires (!view<C>>)
        constexpr C to(R&& r, Args&&... args);                                          // freestanding
      template<template<class...> class C, input_range R, class... Args>
        constexpr auto to(R&& r, Args&&... args);                                       // freestanding
      template<class C, class... Args>
        requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...)
        constexpr auto to(Args&&... args);                                              // freestanding
      template<template<class...> class C, class... Args>
        requires (constructible_from<decay_t<Args>, Args> && ...)
        constexpr auto to(Args&&... args);                                              // freestanding
      […]
    }
    
  2. Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:

    template<class C, class... Args>
      requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...)
      constexpr auto to(Args&&... args);
    template<template<class...> class C, class... Args>
      requires (constructible_from<decay_t<Args>, Args> && ...)
      constexpr auto to(Args&&... args);
    

    -1- Mandates: For the first overload, C is a cv-unqualified class type.

    -2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object]) f that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:

    […]

[2023-11-03; Reflector poll]

Set priority to 3 after reflector poll. Votes split between NAD and using Mandates instead of constraints.

[2023-09-28; Hewill provides alternative wording]

The new wording form is consistent with the current wording, that is, it has a similar structure with 25.7.2 [range.adaptor.object] p1 and 25.7.2 [range.adaptor.object] p8.

Proposed resolution:

This wording is relative to N4958.

  1. Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:

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

    -1- Mandates: For the first overload, C is a cv-unqualified class type.

    -2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object]) f that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:

    1. (2.1) — It has no target object.

    2. (2.2) — Its bound argument entities bound_args consist of objects of types decay_t<Args>... direct-non-list-initialized with std::forward<Args>(args)..., respectively.

    3. (2.3) — Its call pattern is to<C>(r, bound_args...), where r is the argument used in a function call expression of f.

    -?- Remarks: The expression to(args...) is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.