Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't anonymous unions contain members with non-trivial constructors/destructors?

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...

like image 444
Sion Sheevok Avatar asked Dec 09 '10 10:12

Sion Sheevok


People also ask

What is the difference between a union and an anonymous union?

An anonymous union is a union without a name. It cannot be followed by a declarator. An anonymous union is not a type; it defines an unnamed object. The member names of an anonymous union must be distinct from other names within the scope in which the union is declared.

What is a non trivial constructor?

If you define a constructor yourself, it is considered non-trivial, even if it doesn't do anything, so a trivial constructor must be implicitly defined by the compiler.

Can unions have constructors?

A union can have member functions (including constructors and destructors), but not virtual functions.

How do c++ unions work?

In C++17 and later, the std::variant class is a type-safe alternative for a union. A union is a user-defined type in which all members share the same memory location. This definition means that at any given time, a union can contain no more than one object from its list of members.


3 Answers

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.)

like image 111
visitor Avatar answered Oct 19 '22 07:10

visitor


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:

  1. Which object to construct?
  2. If you define a union constructor, how does it construct one object vs another?
  3. Do you have to define one constructor per non-pod member?
  4. What is the syntax for assigning to a different object to the one that is currently assigned?
  5. Do you allow one object to be interpreted in the context of another? This is allowed for unions currently, and is a very common use-case (union { char c[4]; int n; } u; u.n=1234; cout << u.c[1];).
  6. How does the compiler know which destructor to call?
  7. How does the compiler know which copy-constructor to call when copying a union?

(Have I left anything out?)

I suspect it just went into the too-hard basket.

like image 27
Marcelo Cantos Avatar answered Oct 19 '22 08:10

Marcelo Cantos


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:

union X {
    int i;
    std::string s;
};

X x;
x.s = "Hello";
x.i = 23;

No amount of constructors or destructors for X is going to ensure that the assignment in the final line calls ~string before storing 23. 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 write x.i() = 23 instead of x.i = 23.

Or, you can use Boost.Variant.

like image 40
Steve Jessop Avatar answered Oct 19 '22 08:10

Steve Jessop