Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2003-08-27 08:49:02


Joel de Guzman <djowel_at_[hidden]> wrote in message
news:006e01c36c6f$781de2a0$0100a8c0_at_godzilla...
> << pardon me if this gets re-posted >>
>
> Hi,
>
> Is there a reason why we can't allow:
>
> optional<int&> opt;
>
Short answer:

References are not objects; i.e., you can't really have a reference _stored_ somewhere
(even though most implementation do it behind the scenes)

optional<T> contains a copy of a value of type T, that is, an _object_ of type T.
You can't store an object which is a reference, you can only store the referee or a pointer to
it.

(see below for the long answer)

> Also, is there a reason why we can't allow:
>
> variant<int&, double&> var;
>
> IIUC, internally, it's just a matter of storing a boost.reference_wrapper
> if T is a reference.
>
reference_wrapper is simply storing the address of the referee.
optional<T&> _could_ be made to store a T*, that is, a pointer
to the referee, but in that case, the reference itself wouldn't
be optional... the referee would.
Now, if optional<T&> would be really a T* in disguise,
why would you need the optional wrapper when a NULL T* would convey
just the same (specially given that optional<> has
a pointer-like interface).

A reference adds a level of indirection to an _existing_ objects, so,
while you can have or not a reference, you can't have a reference
to an object that might or might not exist.
This is unlike pointers were the null pointer value is specially
given the semantic of 'pointing to nothing'.

If optional<T&> were allowed, it would have to have exactly the same
value semantics as optional<T*>, except that an additional
addressof/dereference would be automatically added.

> In as much as tuple<int&> is allowed, I see no reason why optional
> and variant can't allow references.
>
With a tuple<> the situation is different because tuple<T&> is just
a class with a reference data member, and the language specifically allow
data members to be references but by means of special rules.
Since references are not objects, when you have a reference data member
you're not necessarily adding the data member to the class storage
(even though most implementations do just that).
That is, when you have:

class A { A(int const& a_):a(a_){} int const& a ; } the_A(3);

the implementation is free to bound A::a to the literal 3 _internally_
without never storing the value 3 anywhere; so when you write:

bar(the_A.a);

the compiler is free to replace that with bar(3) without ever accessing the
the_A object, since it is free to keep static track of the objects
to which references are bound to.

The above means that _true_ reference members are supported by means
of special rules. In particular, the requirement that you can't bypass
the initialization of reference data members is of a different nature than
the requirement that you can't bypass the initialization of an object
without a default ctor.
In the latter, the reason is that the compiler cannot tell how to initialize
the storage for an object without a default ctor, but here we can simply leave
the storage uninitialized as long as access to it is protected, as optional<> does.
In the former, however, the reason is that a reference is not an object,
it does not exist by itself, it is just an _alias_ for an object, so you can't
tell the compiler that you have an alias for _some_ object without telling which
object in particular. Here, there's no storage at all to leave uninitialized.

There's no doubt that optional<T&> could be hacked by making it store T*
(or reference_wrapper for that matter); but that would be akward for at least
two reasons:

(a) It won't be the reference but the referee which would be optional

(b) The interface of optional<T&> could not really parallel the interface of
optional<T> since the latter uses 'T const&' in the ctor and 'T&' in
operator *.
One can say that the ctor takes 'an object of type T', where the fact
that this is expressed as 'T' or 'T const&' is an implementation detail,
but this underspecification can be a problem for copy-expensive objects.
But for operator*(), changing its return type from 'T&' to 'T', as it
would be required if 'T' were itself a reference, would change its
semantic (one could say that in this case the two semantics are logically
equivalent, because of the asliasing already involved in optional<T&>,
but although true this is subtle).

IOWs, optional<T&> be have to represent an explicit special case.
Thus, is there any real-world reason you need optional<T&>?
At least you can always use reference_wrapper explicitely, as in
optional< reference_wrapper<T> >.

HTH

Fernando Cacciola


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