Readable types with prvalue reference types erroneously model Writable (original) (raw)

Consider:

struct MakeString { using value_type = std::string; std::string operator*() const { return std::string(); } };

static_assert(!Writable<MakeString, std::string>()); // FAILS

This is a huge usability problem, since it permits users to, e.g., pass a range to sort that is not, in fact, sortable. The Ranges TS inherited this problem from the Palo Alto Report (N3351), which also has this bug. Fixing this will be tricky easy :-) without messing up proxy iterators.

EDIT: See ericniebler/range-v3#573

Resolution Discussion:

One fix would be to require that *o return a true reference, but that breaks when *o returns a proxy reference. The trick is in distinguishing between a prvalue that is a proxy from a prvalue that is just a value. The trick lies in recognizing that a proxy always represents a (logical, if not physical) indirection. As such, adding a const to the proxy should not effect the mutability of the thing being proxied. Further, if decltype(*o) is a true reference, then adding const to it has no effect, which also does not effect the mutability. So the fix is to add const to decltype(*o), const_cast *o to that, and then test for writability.

Proposed Resolution

Accept the wording in P0547R1 (duplicated below).

(Includes the resolution for #387.)

Change the definition of Writable ([iterators.writable]) as follows:

template <class Out, class T> concept bool Writable() {

In [iterators.writable] paragraphs 2.1 and 3, replace "the assignment" with "either above assignment":

(2.1) — If Readable() && Same<value_type_t, decay_t>() is satisfied, then *o after