Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2002-08-11 09:57:13


On Sunday 11 August 2002 03:31 am, Eric Friedman wrote:
> Introducing move semantics is all-good and well, and I think it's
> something Boost should "standardize" anyway, but I think we also need
> to figure out a good way to keep the non-empty invariant for
> boost::variant -- even when its bounded types do not provide the
> nothrow-move guarantee.

Agreed.

> I think the most important point, however, to draw from the proposal is
> that the two classes would really be one and the same if the set of
> bounded types provides nothrow-move semantics.

There is _so_ much overlap between these two classes that I wonder why we
should even separate them? I'm thinking of this from the perspective of
someone who would have to write a C++ style guide: how would you lay down the
rules w.r.t. using safe_variant vs. variant? Use safe_variant always, because
we might not ever be able to afford the strange behavior of empty variants?
Use variant always, because we don't care about exceptions anyway? Or, use
safe_variant when required and variant otherwise?

I think we should stay with one variant class. It should, by default, always
be safe. However, because there are types that can't safely be put into a
variant without compromising speed and/or efficiency, we should:

  1) Make a reasonable decision between the two safe storage methods (heap
allocation vs. doubling the stack allocation). Perhaps use an heuristic such
as: if in a variant<T1, T2, ..., TN>, type Ti is unsafe, then:
    a) if sizeof(Ti) >= sizeof(variant<T1, T2, ..., T{i-1}, T{i+1}, ..., TN>)
then allocate Ti on the heap
    b) otherwise, allocate Ti by doubling the stack allocation.

  2) Optionally warn the user at compile-time when making this type of
decision. This lets the performance-savvy users know if variant is being
pessimistic so they can tweak things (see #3).

  3) Allow wrappers to designate the type of storage used. We already have
incomplete<Ti> for heap allocation. Perhaps a the complete list should be:
    incomplete<Ti> or on_heap<Ti>: allocate on the heap
    on_stack<Ti>: on the stack, potentially doubling the size
    unsafe<Ti>: on the stack, without doubling, but variants may become
singular/empty if an assignment or swap involving this type throws.
    trivially_movable<Ti>: on the stack, and we can move with memcpy (this is
shorthand notation for specializing some move-semantics traits)

This way we've guaranteed safety barring the presence of 'unsafe<T>' but also
optionally warned the user about potential efficiency issues and given
him/her a way to manually resolve the efficiency issues on a type-by-type
basis based on the requirements of his/her project (space may be more
important in one area, whereas speed is more important in another).

        Doug


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