D
darylew
What does the destructor of the union do in this case? If the
last field used was the string, then it needs destruction; if
the last field used was the int, then destruction of the string
element would be undefined behavior. According to the standard,
if and non-static data member of the union has a non-trival
destructor, the corresponding member function of the union must
be user-provided or it will be implicitly deleted for the union.
Implicitly deleting a destructor means that the only thing you
can do with the resulting union is to allocate it dynamically,
and never delete it.
* std::string has a non-trivial destructor
* the automatically-created version of MyData::~MyData is deleted
* there is no manual destructor for MyData
* so MyData is indestructible
* which makes Handle::~Handle deleted by default
* but there is a manually-created version of outer destructor.
* so Handle CAN be used in destructible contexts
(I think. However, this facility would be useless if it that
assertion isn't true.)
To use the union in a class, you need to provide a user defined
destructor. But don't ask me what it should do. It will have
to use an explicit destructor call to destruct the string
member, but only if the string member was the active member
(which is information it doesn't have).
You didn't notice that I did write a manual destructor for Handle?
In fact, I wrote justifications for all my steps. Handle has the tag
for int vs. string. (It can't be in Handle::MyData.)
In fact, what he needs isn't a union, but a variant (from
Boost, or hand written, if you can't use Boost); a discriminate
union, if you prefer.
That is what I wrote; Handle is a discriminated union wrapping the
plain union MyData. The variant from Boost, or hand-written code,
would do something similar to what I did.
For the rest: VC++ doesn't support this feature of C++11 yet,
so portability would be limited. With g++ 4.7.2, it does work
if I define the union as:
Which feature didn't work? Did you try to compile my (untested)
code?
union U
{
int i;
std::string s;
U() : i() {}
~U() {}
};
and do everything with placement new/explicit delete on U::i or
U::s (exactly as you would if you were implementing Variant
yourself). Globally, this feature does make the implementation
of Variant somewhat cleaner---you loose a lot of
reinterpret_cast, and additional members necessary to
(hopefully) ensure alignment. But it probably shouldn't appear
elsewhere; it's too tricky to get right.
Yes on that last sentence.
Daryle W.