iterators that return move-only types by value do not satisfy Readable
ericniebler opened this issue · comments
The problem is with CommonReference<reference_t<I>, value_type_t<I>&>()
. Imagine an iterator I
such that reference_t<I>
is std::unique_ptr<int>
. The CommonReference
first computes the common reference type to be std::unique_ptr<int>
, then it tests that std::unique_ptr<int>&
is ConvertibleTo
std::unique_ptr<int>
, which is of course false.
The fix is to instead be testing CommonReference<reference_t<I>&&, value_type_t<I>&>()
. That causes the common reference to be computed as const std::unique_ptr<int>&
, and std::unique_ptr<int>&
is indeed convertible to that.
Proposed Resolution
(Includes the resolution for #339, accepted by LWG in Kona but not yet moved in full committee).
Change the Readable
concept [iterators.readable] as follows:
template <class I>
concept bool Readable() {
return requires {
typename value_type_t<I>;
typename reference_t<I>;
typename rvalue_reference_t<I>;
} &&
- CommonReference<reference_t<I>, value_type_t<I>&>() &&
- CommonReference<reference_t<I>, rvalue_reference_t<I>>() &&
- CommonReference<rvalue_reference_t<I>, const value_type_t<I>&>();
+ CommonReference<reference_t<I>&&, value_type_t<I>&>() &&
+ CommonReference<reference_t<I>&&, rvalue_reference_t<I>&&>() &&
+ CommonReference<rvalue_reference_t<I>&&, const value_type_t<I>&>();
}
@CaseyCarter comments?
(A "this wording is relative to" would be good - looks like N4651 to me, but reviewers won't know.)
This seems plausible. I'm concerned that I haven't yet developed an intuition for CommonReference
: it seems like I should be able to describe why the old requirement was wrong, and why the new requirement is right, in a sentence. The fact that I cannot - when we're supposedly the experts on this thing - makes me concerned that CommonReference
needs "sharpening."
The way I think of it is that:
common_reference
is the least-qualified reference(-ish) type to which all arguments bind, and- If any of the arguments are not references, the result of
common_reference
is also not a reference.
I could imagine treating non-reference types as if they were rvalue reference types. Then, common_reference_t<int, int>
would be int&&
since an expression e
such that decltype(e)
is int
can bind to an int&&
. That wouldn't mean that common_reference
always evaluates to a reference, though. common_reference_t<int, short>
would still be int
.
I considered this resolution, but treating common_reference_t<Ts...>
as if the user had written common_reference_t<Ts&&...>
seems lossy. If that's what the user wants, he/she can type that.