I may be mistaken, but the basic explanation I've found has been that the union can't initialize because it doesn't know which member's constructor to call. The compiler can not automatically generate a constructor for the union.
Why is the user not allowed to define the unions constructor? This would remove said issue and allow the presence of union members that have a non-trivial constructor/destructor.
Also, why can't a union member have any custom constructors? The previous explanation doesn't stand for custom constructors.
Update 1:
Example:
struct SQuaternion
{
union
{
S3DVector Axis;
struct
{
float X;
float Y;
float Z;
};
};
float W;
};
Note: The issue here seems to be that the union is anonymous. As such, how would one name the constructor of the union? It seems impossible to do so, merely because it has no name, and for no other reason. It'd be a terrible reason if it was a simple lexical issue...
Update 2: Simply by wrapping the offending member in an enclosing anonymous structure, the error disappears. I suppose this is the closest thing one can do with an anonymous union. The fact that it ceases to be an issue still seems strange...
It probably would be possible to define such a thing, but that would open such a can of worms that it almost certainly wouldn't be worth it:
union { char c[4]; int n; } u; u.n=1234; cout << u.c[1];
).(Have I left anything out?)
I suspect it just went into the too-hard basket.
I think you're right, unions in C++ are under-featured. They're pretty much a straight copy of unions from C, which means that they don't serve as variant types for C++.
There's no simple way for
union
in C++ to represent a proper variant type. Consider the following code, if it were legal:No amount of constructors or destructors for X is going to ensure that the assignment in the final line calls
~string
before storing23
. For the compiler to do it, the union would have to contain some kind of indicator what type is stored. That's why everything must be POD. I don't know the reasons for the differences between named and unnamed unions, though, this applies to both.Perhaps C++ unions could have been defined to be like C unions if all their members are POD, but to contain this extra information, and call the correct destructors at the correct times, if any member is non-POD. But this isn't the simple change you proposed.
You can somewhat laboriously write a variant type by writing a class which has a value to indicate the type currently stored, and then the constructors, copy assignment operator, and destructor that you would have put in your union, had you been allowed.
Use a char array for storage, placement new for construction/assignment, and a direct call to the correct destructor in your destructor.
Beware of the alignment issue - you need to ensure that your raw storage is adequately aligned for any of the types you place in it. One way to do this is to dynamically allocate it. Another is to put your char array into a union with whatever built-in type has the greatest alignment requirement (if you don't know: all of them).
The only thing that's different in usage, from the union you want, is that instead of public data members
int a
,float b
,string c
, you'll have to provide accessors that return a proxy object (possibly a reference to the object itself), that is capable of assigning correctly, which means calling the destructor for the old type first. Then you can writex.i() = 23
instead ofx.i = 23
.Or, you can use Boost.Variant.
You've heard right - non-trivial constructors for members suggest the object is meant to encapsulate its data content, and unions remove that encapsulation. Putting a union of simple types into a class is a great way to add encapsulation, and make sure the union content is used in a sensible, safe way.
Re unions having members: from 9.5
This code seems to work fine for me:
A bigger reason would be: how would the union know which destructor to call. The language itself doesn't track which member is active in a union.
It seems that C++0x will allow non-trivial types in unions, in which case you'll be forced to implement your own constructor(s) and destructor. (The latter is a little unclear from the proposal, it seems that the union destructor will not call any member destructors and the destructor for the right one would have to be invoked manually.)