This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
ranges::to
's reserve
may be ill-formedSection: 25.5.7.2 [range.utility.conv.to] Status: C++23 Submitter: Hewill Kang Opened: 2022-07-21 Last modified: 2024-01-29
Priority: Not Prioritized
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with C++23 status.
Discussion:
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 (24.3.4.4 [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
.
[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().
[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4910.
Modify 25.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 ofr
in the following manner:
(1.1) — If
convertible_to<range_reference_t<R>, range_value_t<C>>
istrue
:
(1.1.1) — If
constructible_from<C, R, Args...>
istrue
:C(std::forward<R>(r), std::forward<Args>(args)...)
(1.1.2) — Otherwise, if
constructible_from<C, from_range_t, R, Args...>
istrue
:C(from_range, std::forward<R>(r), std::forward<Args>(args)...)
(1.1.3) — Otherwise, if
(1.1.3.1) —
common_range<R>
istrue
,(1.1.3.2) —
cpp17-input-iterator<iterator_t<R>>
istrue
, and(1.1.3.3) —
constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>
istrue
:C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)
(1.1.4) — Otherwise, if
(1.1.4.1) —
constructible_from<C, Args...>
istrue
, and(1.1.4.2) —
container-insertable<C, range_reference_t<R>>
istrue
: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));(1.2) — Otherwise, if
input_range<range_reference_t<R>>
istrue
:to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(1.3) — Otherwise, the program is ill-formed.