Proposed resolution:
This wording is relative to N4917.
[Drafting Note: Three mutually exclusive options are prepared, depicted below by Option A, Option B, and Option C, respectively.]
Option A: Properly disallow this case (preferred solution)
Modify [range.elements.view] as indicated:
namespace std::ranges { […] template<class T, size_t N> concept returnable-element = // exposition only requires { std::get<N>(declval<T>()); } && is_reference_v<T> || move_constructible<tuple_element_t<N, T>>; […] }
Option B: Relax subrange's get to have more overloads. Since subrange's non-const begin unconditionally moves the iterator (even for lvalue-reference),
[[nodiscard]] constexpr I begin() requires (!copyable<I>); Effects: Equivalent to: return std::move(begin_);
if we add more get overloads, it would work. The non-const lvalue-ref overload would work (and it also moves because non-const lvalue begin moves). This solution would make another way to let subrange's iterator in moved-from state, which is not good.
Modify [ranges.syn] as indicated:
[…] namespace std::ranges { […] template<size_t N, class I, class S, subrange_kind K> requires ((N == 0 && copyable<I>) || N == 1) constexpr auto get(const subrange<I, S, K>& r); template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(subrange<I, S, K>&& r); template<size_t N, class I, class S, subrange_kind K> requires ((N == 0 && constructible_from<I, const I&&>) || N == 1) constexpr auto get(const subrange<I, S, K>&& r); template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(subrange<I, S, K>& r); } […]
Option C: Make subrange's get to return by reference. This seems to significantly change the subrange's tuple protocol, which is not ideal.