ranges::to reserves the wrong size
Hewill Kang

Created on 2022-06-20.00:00:00 last changed 1 month ago


Date: 2022-07-08.20:04:38

Proposed resolution:

This wording is relative to N4910.

  1. Modify [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. ( — common_range<R> is true,

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

        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. ( — constructible_from<C, Args...> is true, and

        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)));
          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-07-15.00:00:00

[ 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."
Date: 2022-06-20.00:00:00

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.

Date User Action Args
2022-07-08 20:04:38adminsetmessages: + msg12562
2022-06-25 19:13:17adminsetmessages: + msg12533
2022-06-20 00:00:00admincreate