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


On Friday 09 August 2002 12:52 pm, David Abrahams wrote:
> > We decided long ago that #1 is bad, because it makes variants harder to
>
> use.
>
> > But if we choose #2, then the current assign/swap semantics don't meet
>
> the
>
> > basic guarantee!
>
> I don't understand why that must be the case. Are we still working with
> assign_as<T>, swap_as<T> et al?
> If so, it seems to me that these can just dispatch to the operation defined
> for T, and assuming T's assignment meets the basic guarantee, all is well.

Because we have to handle the case where we are assigning a T to a U. For
instance:

void foo(T& t, U& u)
{
  variant<T, U> v(t);
  v = u;
}

Prior to the assignment "v = u", we have memory inside 'v' that stores a value
of type 'T'. After the assignment "v = u", that same memory inside 'v' will
store a value of type 'U'. Because the U needs to occupy the same space as
the T does now, the T must be destructed before the U is constructed.
However, U's construction can throw so we need to save the value of T. That's
fine, but how do we _restore_ the saved value of T? For instance, say we do
this:

char the_memory[max_sizeof(T, U)];

variant& operator=(const U& u)
{
  T* asT = reinterpret_cast<T*>(&the_memory[0]);
  T savedT(*asT); // save a copy of the T value. it's okay if this throws
  asT->~T(); // destroy the T, won't throw

  try {
    new (&the_memory[0]) U(u); // might throw
  }
  catch(...) {
    // restore the T value
  }
}

What goes in that catch block? We can't copy-construct savedT into the_memory,
because that might throw and we'd be left with the_memory in an inconsistent
state. We can't use T's assignment operator because there's no T there.

> I don't expect to be able to repair exception-broken types (i.e. those
> whose operations don't even meet the basic guarantee).

That's fine. Unfortunately, the problem above occurs even with types whose
assignment and copy constructor meet the strong guarantee.

        Doug