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.

### 3722. `ranges::to` `reserve`s the wrong size

Section: 26.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2022-06-20 Last modified: 2022-07-08

Priority: 4

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

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

View all issues with New status.

Discussion:

In bullet 1.1.4 of `ranges::to`, if the `Container` satisfies `container-insertable` and `R` models `sized_range`, it will first construct the `Container` with `args...` and then preallocate memory by calling `c.reserve()`.

However, this only makes sense when `c` is default-initialized. If instead the size of the `Container` created by `args...` is not `0`, the value passed into `c.reserve()` will be wrong, for example:

>     ranges::to<std::string>(std::views::single('o'), "hell");

The size of the `string` created by `"hell"` is already `4`, whereas the size of `R` is only `1`, which makes `c.reserve(1)` useless.

[2022-07-08; Reflector poll]

Set priority to 4 after reflector poll. Some suggestions for NAD.

- "The intended use of those additional arguments is for allocators, comparators, max load factors, and the like. Not...this. I can't really bring myself to care about this example - and it adds some (admittedly small) overhead for the by far common case."
- "And it's not the right fix imo. if we wanted to put *some* constraints on Args, we could attempt to do so, although that might be challenging."
- "calling reserve() is just an attempt at an optimization."
- "I disagree with NAD given the fact that we have specifically designed the "reserve" branch of the decision tree."

Proposed resolution:

This wording is relative to N4910.

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

   >     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 `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>) {
   > >    >       using ST = range_size_t<C>;
   > >    >       using CT = common_type_t<ST, range_size_t<R>>;
   > >    >       auto sz = ST(CT(ranges::size(c)) + CT(ranges::size(r)));
   > >    >       c.reserve(szranges::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.