On Mon, Aug 2, 2021 at 10:01 AM Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

I think that's the core of the problem here: the "right semantic" for
references is *not* the "right semantic" for *values*. It makes
absolutely no sense for assignment to an optional value type to
unengage/re-engage. Values are values, and if a value is assignable,
then assigning to it has a clear and unambiguous meaning.

optional<T&> simply treats T& as a value rather than a reference. Once you accept that, you'll see that it's the same semantic.

If you want to have `optional<T&>`'s assignment behave this way, then
it's clear that it cannot be *spelled* `optional<T&>`. This is for the
same reason that `vector<bool>`s mistake is that it's called "vector".
`vector`'s are supposed to have specific behavior, and `vector<bool>`
doesn't follow that. Same goes for `optional<T>`: it has specific,
reasonable behavior which `optional<T&>` wouldn't have.

The comparison to vector<bool> fails really badly. vector<bool> differs from vector<T> in basically all respects - every function has different semantics, vector<bool> isn't a range of bool, it's not contiguous, basically everything does something different.

optional<T&>, on the other hand, has the same behavior as optional<T> in all cases - once you accept that it treats T& as a value. It simply does not have differing semantics, unlike vector<bool>. Generic code on optional<U> does actually work for U being a reference or not - because the semantics are the same. 

And it bears repeating that there is a lot of implementation experience with a rebinding optional reference, with no corresponding vector<bool>-like regret, while there is zero implementation experience with an assign-through optional reference.

If you believe that this is the right semantic for references, then it
needs to be a separate class from optional values.

Consider this. Does an int[10] have different range semantics from std::array<int, 10>? Your argument here is that it does - the latter calls member begin() and end() while the former does something completely different and so if we want to treat int[10] and array<int, 10> the same then we should have different named range-based-for loops to iterate over both.

But they're not different. The fact that we have different syntax to iterate over a C array vs a std::array does not mean that that iteration has different semantics, and we just hide that difference in the language and the library. Likewise, the fact that we have different syntax to change the value of an optional<T&> does not mean that it has different semantics from any other optional<T>. The semantics of p = q are that we change the value of p to be the value of q. Rebinding optional<T&> does that.