ranges::to should prioritize the "reserve" branchSection: 25.5.7.2 [range.utility.conv.to] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-07-17 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 Tentatively NAD status.
Discussion:
When the constructed range object has no range version constructor, ranges::to falls into a
branch designed specifically for C++17-compliant containers, which calls the legacy constructor that
accepts an iterator pair with C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...).
#include <boost/container/vector.hpp>
#include <sstream>
#include <ranges>
int main() {
std::istringstream ints("1 2 3 4 5");
std::ranges::subrange s(std::istream_iterator<int>(ints),
std::istream_iterator<int>(),
5);
auto r = std::ranges::to<boost::container::vector>(s); // discard size info
}
Above, subrange saves the size information of the stream, but ranges::to only extracts
its iterator pair to create the object, so that the original size information is discarded, resulting in
unnecessary allocations.
reserve" branch here because it is really designed for this situation.
[2023-10-30; Reflector poll]
Set status to Tentatively NAD after reflector poll. "This optimizes exotic cases at the expense of realistic cases."
Proposed resolution:
This wording is relative to N4950.
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- Mandates:
Cis a cv-unqualified class type.-2- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(2.1) — If
Cdoes not satisfyinput_rangeorconvertible_to<range_reference_t<R>, range_value_t<C>>istrue:
(2.1.1) — If
constructible_from<C, R, Args...>istrue:C(std::forward<R>(r), std::forward<Args>(args)...)(2.1.2) — Otherwise, if
constructible_from<C, from_range_t, R, Args...>istrue:C(from_range, std::forward<R>(r), std::forward<Args>(args)...)
(2.1.3) — Otherwise, if
(2.1.3.1) —common_range<R>istrue,
(2.1.3.2) — the qualified-iditerator_traits<iterator_t<R>>::iterator_categoryis valid and denotes a type that modelsderived_from<input_iterator_tag>, and
(2.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)...)(2.1.4) — Otherwise, if
(2.1.4.1) —
constructible_from<C, Args...>istrue, and(2.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));(?.?.?) — Otherwise, if
(?.?.?.?) —
common_range<R>istrue,(?.?.?.?) — the qualified-id
iterator_traits<iterator_t<R>>::iterator_categoryis valid and denotes a type that modelsderived_from<input_iterator_tag>, and(?.?.?.?) —
constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>istrue:C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(2.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)...);(2.3) — Otherwise, the program is ill-formed.