[optional] self-assignment and assignment of references
Hi people,
Recently, here:
http://lists.boost.org/MailArchives/boost/msg78947.php
Joe Gottman pointed out that optional<T> fails on aliasing situations
like self-assignment, and proposed to forward assignment to
T's assignment operator (when the lhs is initialized).
In the current implementation, Optional's assignment uses a
destroy + copy-construct pattern.
But in the upcoming 1.33 release, it will forward the assignment
to T::operator=(T const&) whenever the optional<T> is originally initialized
AND when T is not a reference.
This is a slight change that I doubt would cause any troubles in practice.
However, that change brings up a long standing issue: assignment of optional
references.
You may have noticed that assignment upon optional
"Fernando Cacciola"
Hi people,
Recently, here:
http://lists.boost.org/MailArchives/boost/msg78947.php
Joe Gottman pointed out that optional<T> fails on aliasing situations like self-assignment, and proposed to forward assignment to T's assignment operator (when the lhs is initialized).
In the current implementation, Optional's assignment uses a
<snip>
Rationale for Boost.Optional assignment semantics:
<snip>
Following the logical expectation of requiring optional<T> to follow T as much as possible, one would expect:
ora = orb ;
to change the value of the referee (a) to that of 'b'
But there is a catch:
As the song goes, optional<T> can follow T down but not that far. :-)
The reason is that assignment must operate on optional<T> itself, so it must be well defined in the case it is uninitialized, and there it just can't follow T's behaviour.
If 'ora' is uninitialized, 'ora = orb' can ONLY rebind the wrapped reference to 'b' if you expect any kind of equivalence to be the postcondition of the assignment. It clearly can't just ignore the assignment cause there is no referee to change its value.
I don't have a strong opinion about which semantics optional<T> should adopt, but IMO this logic is a flawed way to justify your choice. An optional<T> (where, in this case, T = U&) can be viewed as a variant type: T | void An uninitialized optional<T> takes the void branch of the "or." When orb holds a U& rather than a void, ora = orb is essentially replacing the void with a new U& and initializing it with whatever is in orb. Some (sensible, correct) choices simply can't be justified on the basis of logic. -- Dave Abrahams Boost Consulting www.boost-consulting.com
"David Abrahams"
"Fernando Cacciola"
writes: Hi people,
Recently, here:
http://lists.boost.org/MailArchives/boost/msg78947.php
Joe Gottman pointed out that optional<T> fails on aliasing situations like self-assignment, and proposed to forward assignment to T's assignment operator (when the lhs is initialized).
In the current implementation, Optional's assignment uses a
<snip>
Rationale for Boost.Optional assignment semantics:
<snip>
Following the logical expectation of requiring optional<T> to follow T as much as possible, one would expect:
ora = orb ;
to change the value of the referee (a) to that of 'b'
But there is a catch:
As the song goes, optional<T> can follow T down but not that far. :-)
The reason is that assignment must operate on optional<T> itself, so it must be well defined in the case it is uninitialized, and there it just can't follow T's behaviour.
If 'ora' is uninitialized, 'ora = orb' can ONLY rebind the wrapped reference to 'b' if you expect any kind of equivalence to be the postcondition of the assignment. It clearly can't just ignore the assignment cause there is no referee to change its value.
I don't have a strong opinion about which semantics optional<T> should adopt, but IMO this logic is a flawed way to justify your choice. An optional<T> (where, in this case, T = U&) can be viewed as a variant type:
T | void
An uninitialized optional<T> takes the void branch of the "or." When orb holds a U& rather than a void,
ora = orb
is essentially replacing the void with a new U& and initializing it with whatever is in orb.
I suppose you're saying that given what you wrote, and the fact (left implicit) that when 'ora' takes the 'T' branch a variant type could just assign the referred value but not rebind, then the choice of what to do in the already initialized case just don't follow directly from the only possible option in the uninitialized case. If that's the case, you're right, though I didn't intend to make my choice appear as a logical consecuence of the constrain (when uninitialized it must bind to the reference). I rather intended to show the constrain as a mere motivation for the choice. Clearly others choice are still valid. Fernando Cacciola SciSoft
"Fernando Cacciola"
I suppose you're saying that given what you wrote, and the fact (left implicit) that when 'ora' takes the 'T' branch a variant type could just assign the referred value but not rebind, then the choice of what to do in the already initialized case just don't follow directly from the only possible option in the uninitialized case.
I think that's what I'm saying, yes.
If that's the case, you're right, though I didn't intend to make my choice appear as a logical consecuence of the constrain (when uninitialized it must bind to the reference).
Actually when uninitialized you can view it as though the reference isn't there to begin with. In fact, that's probably the correct view since that reference would be an uninitialized one, which as we know is not a valid state for a C++ program to be in.
I rather intended to show the constrain as a mere motivation for the choice. Clearly others choice are still valid.
I think you may be able to legitimately motivate the choice on the grounds of usability or lack of error-prone-ness, but I don't think the constraint works as a motivation. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (2)
-
David Abrahams
-
Fernando Cacciola