Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2003-10-30 14:41:54


Hi,

I'd like to poll the list on an issue I'm not sure how to resolve.

Consider:

int a = 1 ;
optional<int&> opt(a) ;

Ideally, initialized optional references should behave just like true
references, thus, assignment should not rebind the reference to a different
object but assign right into the referenced object:

int b = 2 ;
opt = b ;
assert( a == b) ;

Currently, optional support for references _does not_ work like this:
assignment rebinds the reference:

opt = b ;
b = 3 ;
assert( *opt == 3 );

Joel de Guzman pointed out to me that the current semantics look wrong since
they are different from true reference semantics.
However, true reference semantics introduces the following subtle issue:

Assignment to uninitialized optional reference.
Consider,

optional<int&> def ;
int a =1 ;
def = a ;

what should the last assignment do?
It cannot do as described above (as true references) since there is no
referenced object to assign to.
Possibilities that come to mind are:

a)
Treat "assignment to uninitialized optional" as "copy initialization":
That is,
Given optional<T> o ;
o = v <==> o = optional<T>(v) whenever 'o' is uninitialized.

b) Leave the semantics undefined (and use BOOST_ASSERT)
c) Fail (via an exception such as invalid_ref_assignment())
d) Trap: call (unqualified) a function "trap_invalid_ref_assignment()", then
do as (i).

The problem with (a) is that the semantics of reference assignment become
dual: it might assign the referenced object or bind (for the first time).
And this without lexical support (unlike true copy initialization there is
no "syntax" to tell the difference).
OTOH, (a) is how non-reference optionals work: assignment to uninitialized
is initialization.

(b) just pushes the problem _completely_ to the user, but allows
BOOST_ASSERT tailoring to put some control on the user side.

(c) Defines the semantics quite clearly, but I'm not sure if failing the
assignment is really useful.

(d) Let the assignment succeed, which might be very important depending on
the domain, yet let users trap the 'problem' if it actually is a problem in
a specific application. The problem with (d) is that the trap is not a
per-instance policy but a global property of all optional<> types, which
will be a problem if different 'domains' with differing functional
requirements coexist.
I'm more than reluctant to add the clumsiness of a policy parameter just to
solve this problem.

It is worth to notice that: Given,

int a = 1 ;
optional<int&> opt(a);
optional<int&> def ;
opt = def ; (or opt = boost::none) from the latest CVS.

The last assignment 'unbinds' the reference (it doesn't assign to nor
destroy the referenced object).
I see this as the only sensible thing to do in this case which indicates
that "transitional" assignemnts -those which toggles between
initialized/uninitialized states- do require special semantics, at least in
one of the directions.

Based on the last observation I'm inclined to choose option (a).

I'd like to know what do you think before I resolve the matter.

(BTW, this won't happen until after 1.31.0, which will retain current
'always rebinding' semantics).

Fernando Cacciola
SciSoft


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk