Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2003-11-07 15:02:13


Contrary to what optional<>'s interface reflects, I do strongly agree with
the motto "there should be one and only one obvious way to do it".
The reason why optional<>'s interface provides more than one way is because
the way that was obvious to me when I submitted it to review is far from
obvious to too many people to ignore it, so eventually I ended up putting
some duality in the interface with the hope that actual experience will
shift the balance toward a proper lean interface.

In the particular case of direct value assignment, it's interface evolved as

Originally, there was only *opt=v, which used a proxy to handle
uninitialized assignment.
But then everybody assumed that the above expression was undefined whenever
opt is uninitialized since (*opt) itself is so.
I realized that this assumption was correct since the whole purpose of
operator '*' is to clue about the undefined behavior of accessing or
manipulating uninitialized optionals.
IMO, the fact that you can actually write (*opt)=val
(or even opt.get() = val) is a consequential side effect of the fact that
operator*() ( or get()) returns an lvalue; but I don't see it as a part of
optional<>'s interface w.r.t to direct value assignment.
(just as sequence.front() = val ; is not part of the sequence interface).

Ever since the beginning there was a fundametal ultimate question that keeps
evading a complete answer: What is optional<>?
It has been called a pointer (clearly wrong btw), a discriminated union,
a container, a specialized T, and everything in betweem.
However, one question that has an easy answer is: What is the purpose of
It's only purpose is wrap a value of type T which might not exist.
>From this purpose it logically follows that for the case when an optional<T>
is in fact initialized, it should behave as much as a T as possible or else
don't behave at all and simply give access to the underlying T object.

Therefore, given a type T and a *initialized* optional<T>, if the semantics
of "(*opt)=val"/"opt.get()=val" are well defined
(definition which is a property of T rather than of optional<T>),
then -leaving syntax aside- the semantics of optional<>'s
direct value assignment should mirror that, or, alternatively, there should
be no such operation.

IMO, considering optional<>'s purpose, a direct value assignment operation
is highly productive and should be supported by the interface.
Right after formal review this operation was supported by opt.reset(val)
{and still is}, but I still believe that the preferred syntax
(using operator=(val)) is sound and boosts the productivity once the
semantics are well understood.
(specially now that you can write: "opt = none" to reset() opt)

Syntax aside, these arguments settles (IMO) the issue of optional's direct
value assignment semantics for the case of T being a reference type and the
lhs optional<> being initialized.

Still, there is a gap in the functionality not covered by the arguments
layed out so far: what are the semantics of assignment to an *uninitialized*
Again looking at optional<>'s purpose (rather then nature which is still
unclear), it seems to me that such an assignment should not fail or be left
undefined, and that it should initialize the lhs.
This definition leaves direct value assignment effectively equivalent to
optional<>'s assignment regardless of the previous state of the lhs:

opt = val <=> opt = optional<T>(val);

which IMO is a desirable feature.

Unless someone raises any objections, this semantics will be reflected in
the versions right after the 1.31.0 release.

Fernando Cacciola

Boost list run by bdawes at, gregod at, cpdaniel at, john at