This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
ranges::to may cause infinite recursion if range_value_t<C>
is a non-move-constructible rangeSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: S. B. Tam Opened: 2023-11-08 Last modified: 2024-03-11
Priority: 3
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
25.5.7.2 [range.utility.conv.to]/2 says:
(2.1) — If
Cdoes not satisfyinput_rangeorconvertible_to<range_reference_t<R>, range_value_t<C>>istrue:
[…]
(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)...);[…]
That is, if range_reference_t<R> is not convertible to range_value_t<C>, and
range_reference_t<R> is an input range, views::transform is applied to convert the
inner range to range_value_t<C> (through to<range_value_t<C>>), and then
the transformed range is converted to C (through to<C>).
#include <ranges>
struct ImmovableRange {
ImmovableRange(int*, int*);
ImmovableRange(ImmovableRange&&) = delete;
int* begin();
int* end();
};
struct C {
ImmovableRange* begin();
ImmovableRange* end();
};
using R = int[1][2];
void test() {
(void)std::ranges::to<C>(R{});
}
Here:
convertible_to<range_reference_t<R>, range_value_t<C>> is false.
range_reference_t<R> satisfies input_range.
range_reference_t<R> can be converted to range_value_t<C> through
to<range_value_t<C>>. (If it couldn't, an error would be produced immediately.)
So to<C> is called recursively, constructing C with the transformed range (whose
range_reference_t<R> is the same as range_value_t<C>). For the construction
from the transformed range:
range_reference_t<R> and range_value_t<C> are both ImmovableRange.
convertible_to<range_reference_t<R>, range_value_t<C>> (i.e.
convertible_to<ImmovableRange, ImmovableRange>) is false.
range_reference_t<R> (i.e. ImmovableRange) satisfies input_range.
range_reference_t<R> can be converted to range_value_t<C> through
to<range_value_t<C>>.
So to<C> is called recursively again, transforming the range for the second time. This time,
the transformation does not change any of the above four facts. As a result, to<C> is called
yet again, leading to an infinite recursion.
to<C> recursively when range_reference_t<R>
is the same as range_value_t<C>.
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
"Do we want same_as or !different-from?"
Proposed resolution:
This wording is relative to N4964.
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.2) — Otherwise, if
same_as<range_reference_t<R>, range_value_t<C>>isfalseandinput_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.