Boost logo

Boost :

From: Eric Friedman (ebf_at_[hidden])
Date: 2003-08-30 02:45:32


Eric Friedman wrote:
[snip]
> ...there are alternative (albeit less elegant) options.
[snip]

I'd like to present what are IMO the most viable options for manually
disabling variant's double storage.

Option #1:

(I presented a very similar strategy to this some time ago at
http://aspn.activestate.com/ASPN/Mail/Message/1316541, but I called it
small_variant at the time.)

Designate void as a flag that signals to variant to disable the double
storage technique. That is, variant<T1, ..., void, ..., TN> will always
operate with single storage. Of course, as I discussed in my previous
message, this means that a failed assignment/swap may result in an
'uninitialized' state for these variants since void is not a meaningful type
of content.

Any variant in such an uninitialized state will be called 'singular.' The
*only* valid operations on a singular variant are variant::operator=,
variant::~variant, and the variant 'query' methods (i.e., which, empty, and
type). The semantics of the query operations will change for singular
variants: variant::which will return -1, variant::empty will return true,
and variant::type will return typeid(void).

Note that under this option, boost::empty would still be usable in variant,
but only in a generic fashion. That is, boost::empty would simply exist as a
convenience, and no special status would be conveyed to variant<T1, ...,
boost::empty, ..., TN>. If we ultimately adopt this option, we might even
seek another name for boost::empty to avoid confusion.

Option #2:

Designate boost::empty as a flag to disable the double storage technique.
Like option #1 above, variant<T1, ..., boost::empty, ..., TN> will always
operate with single storage. However, unlike option #1 above, any failed
assignment/swap may result in the construction of boost::empty content in
its place.

The advantage of this approach is that there are no messy unitialized states
and no singular variants. All operations on variants are always
well-defined.

The disadvantage of this approach is that while boost::empty is an otherwise
perfectly typical type, its use in a variant will dramatically alters the
variant's semantics, a point which may not be immediately clear to
unsuspecting users.

A third point, which is a mixed bag, is that all visitors to such variants
will need to explicitly handle boost::empty as a possible type of content.
Certainly, this is an advantage in terms of type safety. But it may be a
nuisance to those users who want to simply ignore the possibility of an
empty variant -- except in the immediate aftermath of a failed assign/swap.
I imagine option #1 would appeal to these users.

Option #3:

Provide both option #1 and option #2. Naturally, this introduces all the
advantages and disadvantages of the two options, which may be a good thing
in terms of flexibility. Of course, it complicates the semantics of using
variant even further.

---
Finally, I'd like to extend one warning against all of the above options, as
well as any other proposal that selectively abandons the double storage
technique.
Namely, there may arise some situations in generic code where such
customizability of variant's exception safety guarantees (or invariants, if
you wish to see the matter in that light) will result in semantics
unanticipated by either the generic algorithm author or client. Quite
frankly I can't imagine any such situations, but I don't believe the limited
extent of my imagination should assure many <g>.
As always, I'd appreciate feedback on this (unfortunately long) message.
Thanks,
Eric

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