ericniebler / stl2

LaTeX and Markdown source for the Ranges TS/STL2 and associated proposals

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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>&>();
}

(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:

  1. common_reference is the least-qualified reference(-ish) type to which all arguments bind, and
  2. 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.