Title
ranges::to's reserve may be ill-formed
Status
new
Section
[range.utility.conv.to]
Submitter
Hewill Kang

Created on 2022-07-21.00:00:00 last changed 2 weeks ago

Messages

Date: 2022-07-24.10:40:56

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:

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

      4. C(from_range, std::forward<R>(r), std::forward<Args>(args)...)
      5. (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:

        4. C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)
      6. (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(static_cast<range_size_t<C>>(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-21.00:00:00

When the "reserve" branch is satisfied, ranges::to directly passes the result of ranges::size(r) into the reserve call. However, given that the standard only guarantees that integer-class type can be explicitly converted to any integer-like type ([iterator.concept.winc] p6), this makes the call potentially ill-formed, since ranges::size(r) may return an integer-class type:

#include <ranges>
#include <vector>

int main() {
  auto r = std::ranges::subrange(std::views::iota(0ULL) | std::views::take(5), 5);
  auto v = r | std::ranges::to<std::vector<std::size_t>>(0); // cannot implicitly convert _Unsigned128 to size_t in MSVC-STL
}

We should do an explicit cast before calling reserve.

History
Date User Action Args
2022-07-24 10:40:56adminsetmessages: + msg12605
2022-07-21 00:00:00admincreate