concat_view
rejects non-movable referencesSection: 25.7.18.2 [range.concat.view] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-06-24
Priority: 4
View other active issues in [range.concat.view].
View all other issues in [range.concat.view].
View all issues with New status.
Discussion:
In order to prevent non-equality-preserving behavior of operator*
and iter_move
,
concat_view
introduces the concat-indirectly-readable
concept, part of which is:
template<class Ref, class RRef, class It> concept concat-indirectly-readable-impl = // exposition only requires (const It it) { { *it } -> convertible_to<Ref>; { ranges::iter_move(it) } -> convertible_to<RRef>; };
This isn't quite right because convertible_to
checks is_convertible_v
which doesn't
understand copy elision. This makes the current concat_view
unable to work with ranges whose
reference is non-movable prvalue:
auto r = std::views::iota(0, 5) | std::views::transform([](int) { return NonMovable{}; }); auto c1 = std::ranges::concat_view(r); // ill-formed, concat_indirectly_readable not satisfied auto c2 = std::ranges::concat_view(r, r); // ditto
Since std::visit<R>
is used in the implementation to perform reference conversion for the
underlying iterator, the more accurate one should be is_invocable_r
which does understand guaranteed
elision.
join_with_view
has the same issue because compatible-joinable-ranges
requires that the value_type
of the inner range and pattern range must satisfy common_with
,
which always fails for non-movable types. However, this can be automatically resolved by LWG
4074's resolution.
[2024-06-24; Reflector poll]
Set priority to 4 after reflector poll. "Proposed resolution loses the existing requirement that the conversion is equality-preserving." "Don't care about rejecting non-movable reference types."
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.18.2 [range.concat.view] as indicated:
-1- The exposition-only
concat-indirectly-readable
concept is equivalent to:template<class Ref, class CRef> concept concat-ref-compatible-with = is_invocable_r_v<CRef, Ref()>; // exposition only template<class Ref, class RRef, class It> concept concat-indirectly-readable-impl = // exposition only requires (const It it) { { *it } ->convertible_toconcat-ref-compatible-with<Ref>; { ranges::iter_move(it) } ->convertible_toconcat-ref-compatible-with<RRef>; }; template<class... Rs> concept concat-indirectly-readable = // exposition only common_reference_with<concat-reference-t<Rs...>&&, concat-value-t<Rs...>&> && common_reference_with<concat-reference-t<Rs...>&&, concat-rvalue-reference-t<Rs...>&&> && common_reference_with<concat-rvalue-reference-t<Rs...>&&, concat-value-t<Rs...> const&> && (concat-indirectly-readable-impl<concat-reference-t<Rs...>, concat-rvalue-reference-t<Rs...>, iterator_t<Rs>> && ...);