Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2005-03-11 10:39:44

"Brock Peabody" <brock.peabody_at_[hidden]> escribió en el mensaje
> Hi Fernando,
Hi Brock

> I don't know if you saw the rest of the discussion but I think that the
> consensus was that rebinding should be disabled on optional<T&> as you
> suggest, and along with it operator=. Optional<T&> would then only be
> initialized via constructor (like T& itself), and optional<T&>::operator=
> would be a compile time error rather than deferred to T::operator=. Does
> this seem right to you?
I'm not sure...
I don't see a concensus on disabling operator= altoghether.
That's overkill I think.

But I definitely see your point about:

int a = 1 ;
int b = 2 ;
int& ra = a ;
int& rb = b ;
optional<int&> opt(ra);
opt = none
opt = rb ; // rebinds in this case
opt = ra ; // but not in this case!

And I do see this as a problem.

Disabling assignment to references altoghether would definitely solve this
ambiguity but it can be too much of a change.
Generic code just won't work anymore
(because in generic code your T might happen to be a U&)

I really think that optional<T&> should try to follow T& as much as
possible, but you've shown that this can't be done completely
because the effect of assignment directly depends on the original
initialization state (that's why you drew a connection between being
nullable and rebindable).

The choices are:

(1) Make optional<T&> non-assignable

    Pros: Gives optional's assignment a uniformly defined meaning.
    Cons: Badly breaks generic code.
            Badly because such code would be _forced_ to change
            completely as there won't by any way to make it work.
            If the final users feeds in a reference they'll get
            a compiler error unless the code uses *opt =val yet
            only when appropiate, which is an unlikely situation
            becuase if the value is going to be initialized before
            any assignment is _needed_ then probably optional<>
            isn't needed to begin with

 (2) Keep rebinding assignment

     Pros: Gives optional's assignment a uniformly defined meaning.
     Cons: It doesn't do as T& does, which means that current code is:
             (a) probably wrong (users are not aware of the odd semantic)
             (b) intentionally rebinding, which is odd if not unlikely
             (c) going to the trouble of using *opt=val to "fix" the bad
                 semantics and exercising some rule about what to do if opt
                 is uninitialized (which is very complex anyway)

(3) Forward to operator=() WHEN THE LHS IS INITIALIZED or rebind when not

    Pros: Does as T& does when the lhs is initialized.
    Cons: Does NOT do as T& does when the lhs in uninitialized
           This probably means that client code still won't be able
           to "just" use operator=() without any regards to the
           prior initialization state, for if they do, the results
           are probably wrong.

The way I see it:

With choice (1) generic code won't compile unless
it _only_ uses *opt=val to do assignment.
With choice (3) generic code will compile but it might
not do the right thing if it does not take into
account the fact that the effect of assignment changes
depending on the lhs state.

I think that both (1) and (2) _requires_ the code to
discriminate the initialization state prior to
assignment, meaning that the the code will have to be
of the form:

if (!opt)
     [do something, but being aware that if opt wraps a reference
      you'll rebind it]
else *opt = val ;

The '*' is not required with choice (3) yet it is whith choice (1)
In either case, ommitting it is "unnecesary" since this branch executes
when opt is initialized.

I don't think it is possible to avoid the pattern above and "just assign"
unless you're willing to have differing effects depending on whether
opt is initially empty or not. (and most likely you should NOT be willing
to accept that)

With choice (2) it is possible for generic code to "just assign":
if it happens to be OK to rebind.
If it isn't, user code must follow the same pattern above.

Well, if my analysis was correct, choice (2) is the only one that
doesn't _force_ ALL generic code to follow the pattern above.
Thus, in spite of the fact that it breaks the rule "do as T& does"
(becasue it can't follow it completely), it is the least impacting

What do you think?


Fernando Cacciola

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