Boost logo

Boost :

From: Joel de Guzman (joel_at_[hidden])
Date: 2003-10-30 20:50:47

Brian McNamara <lorgon_at_[hidden]> wrote:

> Now, if you want code like this:
>> int a = 1 ;
>> optional<int&> opt(a) ;
>> int b = 2 ;
>> opt = b ;
>> assert( a == b) ;
> to work, you should be writing
>> int a = 1 ;
>> optional<int&> opt(a) ;
>> int b = 2 ;
>> *opt = b ; // Note '*'
>> assert( a == b) ;
> instead. That is, if you want to change the value that is referenced
> (rather than rebind the reference), you should say so explicitly.

You got it the other way around. If you want to rebind the reference,
you should say so explicitly:

    int a = 1 ;
    optional<int&> opt(a) ;
    int b = 2 ;
    opt = optional<int&>(b); // Note '*'

Your case would **kill** generic code. Optional references must be usable
as aliases to T. That's the only meaningful use-case for an Optional
reference. Why would I bother using an optional reference if it merely
acts like a pointer. If that was what I wanted, I'd use a pointer.

> Put another way: If you assign to an optional<T>, the value of the T
> will change. In the case above, T is a reference, and thus assigning
> to an optional<T> means that the "value of the reference" (that is, the
> memory location it addresses) will change.
> This jives with the view of "optional as a container" or "optional as a
> pointer", which is the view I prefer, although I am not sure it is a
> popular one. I think it's unreasonable to try to make an optional<T>
> behave like a T, because it's not one. "*o" is a T; "o" isn't.

AKKKK! Here we go again! Yikes ;-) This view is severely flawed, IMO.
If you religiously regard optional as a container, then why allow optional
references (you clearly can't have vector<T&>)?

Ok... that said, my opinion strongly remains firm. Unless you can provide
a use case that will elevate this matter beyond "matter of taste", I won't
be swayed. Here's my use-case:

Spirit parsers return a thing called a match<T> that reports the
success (or failure) of a parse. Each parser has an attribute (return
type T) which is the result of the parse. An int-parser for example
returns an int. This parser attribute is passed to the semantic action
whenever there is one attached. The semantic action is executed
*only* on a successful match:

    void foo(int);

    r = int_p[foo];

foo is invoked only when an integer is recognized from the input.

Obviously, since a match<T> can be an unsuccessful match, it is best
to implement T, internally, as an optional<T>. The match's attribute
in this case is undefined (invalid). The semantic action never sees
this state because the SA (e.g. foo) can never be called when the parse
is unsuccessful.

Now, consider a symbol-table parser. A symbol table has a slot T
for each symbol (like a map). This data-slot T is mutable (like a map).
A reference to this data-slot must be passed to the semantic action.
Obviously, the data-slot should be passed by reference:

    void bar(int&);

    symbols<int> s;
    s = "hello", "world";

    r = s[bar];

Here, we want *s* to return a match<T&>. We want it to act like a
true reference so that we won't have to write special code for the
handling of reference attributes. The match attribute handling
code and the semantic action handling code does not care about the
type of the attribute. It just passes the attribute to the semantic action
transparently IFF the parse is successful.

So my requirements is that a match<T&> must alias its source T. It
would be uterly error-prone (silent, confusing and hard to debug) if
somewhere, it is re-seated to reference another T:

    match<T> m(i);
    m.value(x); // ok, set the attribute to x

    match<T&> m(r);
    m.value(x); // NO, please don't reset the reference, that's not
                         // what I want! references should only be set
                        // *at construction time*. I want the thing referenced
                        // by r set to x.

Joel de Guzman

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