-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<ranges>
: Avoid infinite recursion in ranges::to
when the inner ranges match but the destination container is still not constructible
#4142
Conversation
Thanks for the fix and the detailed explanation! |
Hum, the nonmovable range always seems problematic to me (although #include <ranges>
struct ImmovableRange {
using value_type = int;
ImmovableRange();
ImmovableRange(ImmovableRange&&) = delete;
int* begin();
int* end();
void push_back(value_type);
};
using R = int[1];
void test() {
(void)std::ranges::to<ImmovableRange>(R{}); // hard error
} |
I don't understand what this comment is saying about the change in this PR. |
Not sure why I made this comment in the first place. if constexpr (_Ref_converts<_Rng, _Container>) {
if constexpr (/* */) {
// ...
} else if constexpr (/* */) {
// ...
} else if constexpr (/* */) {
// ...
} else if constexpr (/* */) {
// ...
} else {
static_assert(_Always_false<_Container>, "the program is ill-formed per N4950 [range.utility.conv.to]/2.3");
}
// ...
} else if constexpr (input_range<range_reference_t<_Rng>>) {
// ...
} else {
static_assert(_Always_false<_Container>, "the program is ill-formed per N4950 [range.utility.conv.to]/2.3");
} instead use a less-nested but repeatedly-checked structure? if constexpr (_Ref_converts<_Rng, _Container> && /* */) {
// ...
} else if constexpr (_Ref_converts<_Rng, _Container> && /* */) {
// ...
} else if constexpr (_Ref_converts<_Rng, _Container> && /* */) {
// ...
} else if constexpr (_Ref_converts<_Rng, _Container> && /* */) {
// ...
} else if constexpr (!_Ref_converts<_Rng, _Container> && input_range<range_reference_t<_Rng>>) {
// ...
} else {
static_assert(_Always_false<_Container>, "the program is ill-formed per N4950 [range.utility.conv.to]/2.3");
} I think it is more appropriate to strictly adhere to the first structure especially after the editorial enhancements #6568 (Although both forms are currently equivalent). |
I'm speculatively mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed. |
I don't recall now why I used this structure, maybe it was simply to avoid repeating the |
Thanks for fixing this bug where for your PR I commented, Thanks for fixing this bug where for your PR I commented, Thanks for fixing this bug where for your PR I commented, 😹 🎉 🤪 |
Fixes GH-4141.
The standard says ([range.utility.conv.to]):
So
views::transform
shouldn't be used ifconvertible_to<range_reference_t<R>, range_value_t<C>>
istrue
.Notably, when step (2.2) recursively calls
ranges::to<C>
, it passes atransform_view
whoserange_reference_t
is the same asrange_value_t<C>
. So in this caseconvertible_to<range_reference_t<R>, range_value_t<C>>
isconvertible_to<range_value_t<C>, range_value_t<C>>
, which should usually be true, which prevents infinite recursion.But,
convertible_to<range_value_t<C>, range_value_t<C>>
may be false (i.e.range_value_t<C>
may not be move-constructible):This PR does not fix this case, as I can't find where the standard addresses this. Maybe it really is a defect in the standard after all.