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

Created on 2022-07-21.00:00:00 last changed 13 months ago

Messages

Date: 2022-11-17.00:42:33

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. (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>)
            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-11-12.00:00:00

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

Date: 2022-08-15.00:00:00

[ 2022-08-23; Reflector poll ]

Set status to Tentatively Ready after six votes in favour during reflector poll.

Are we all happy that the result of conversion to the container's size type may be less than the length of the source range, so the reservation is too small but we don't realize until pushing the max_size() + 1st element fails? I think it's acceptable that converting pathologically large ranges to containers fails kind of messily, but I could imagine throwing if the range length is greater than container's max_size().

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
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2022-11-17 00:42:33adminsetmessages: + msg13057
2022-11-17 00:42:33adminsetstatus: voting -> wp
2022-11-08 03:46:49adminsetstatus: ready -> voting
2022-08-23 15:24:34adminsetmessages: + msg12692
2022-08-23 15:24:34adminsetstatus: new -> ready
2022-07-24 10:40:56adminsetmessages: + msg12605
2022-07-21 00:00:00admincreate