
Fernando Cacciola wrote:
Reece Dunn wrote:
Fernando Cacciola wrote:
Sam Partington wrote:
[...]
Why not use partial specialization so that optional< T > has the semantic sugar, but optional< T & > does not? Or is this already the case?
I'm not too fond of template classes that support different interfaces according to the properties of the template parameters. That just doesn't play along with generic programming where the type you optionalize can or cannot be a reference. Think of vector<bool> for instance. I rather keep looking for a consistent solution.
The difference being that vector<bool> has semantic (but not syntactic) differences, while optional<T&> would have syntactic (but not semantic) differences. And template <class T> optional<T> foo(optional<T> ot) { ... ot = optional<T>(someTexpr); ... } would always work. Just the same as always using iterator_traits<It>::value_type, not It::value_type. Personally, though, I think dropping optional<T&> would be perfectly acceptable. References behave differently to every other built-in type.
A related note about the use of operator *
You said that, even for optional<T&>, it is clear that this:
*o = i;
is UB if 'o' is uninitialized. And so there is no problem defining that assignment as really just the assignment of the underlying type, which in the case of optional<T&> doesn't rebind.
Now I wonder, if instead of operator*, we used value() (or whatever), would it be just as clear?
// Nullable interface
*o = rb ; // assing 'b' to 'a' o = none ; // releases 'a' *o = rc ; // !!! UB !!! Cleary IMO
// Container interface
o.value() = rb ; // assing 'b' to 'a' o.reset() ; // releases 'a' o.value() = rc ; // !!! Still UB !!! �But cleary enough?
I personally prefer the nullable interface as it is more intuitive w.r.t. smart pointer usage. o.value() = b reads to me like value() is returning a (temporary) return value, then you are assigning rb to that.
Me too!
It looks uglier, that's for sure. But that would suggest: int a, b; // set... int& ra = a; int& rb = b; optional<int&> o = ra; // OK, binding *o = rb; // OK, sets a to b o = none; *o = rb; // UB -- attempt to set none with value of rb o = rb; // not UB -- rebind o to b o = ra; // rebind again? o = b; // rebind to refer to b? This is starting to look *exactly* like a pointer, but more confusing. What's the point? (pun intended, unfortunately) Also: template <class A, class B> void foo() { optional<A> oa; optional<B> ob; A a = ...; B b = ...; // then code using any of: oa = b; oa = a; ob = a; ob = b; oa = ob; ob = oa; } foo<int&, int>(); foo<int, int&>(); How is the person who writes that supposed to keep up with what binds to what? [...]