Why can't anonymous unions contain members wit

2019-04-08 13:44发布

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

5条回答
Explosion°爆炸
2楼-- · 2019-04-08 13:50

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.

查看更多
趁早两清
3楼-- · 2019-04-08 13:52

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.

查看更多
乱世女痞
4楼-- · 2019-04-08 13:54

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

A union can have member functions (including constructors and destructors), but not virtual (10.3) functions. A union shall not have base classes. A union shall not be used as a base class.An object of a class with a non-trivial constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4), or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a member of a union, nor can an array of such objects

查看更多
Luminary・发光体
5楼-- · 2019-04-08 14:09

This code seems to work fine for me:

typedef union uAA {
    double dVal;
    int iVal[2];

    uAA() : dVal(3.22) {}
} UAA;

main() {
    UAA rdata;

    printf("Array output: %d %d \nDouble output: %lf \n",
        rdata.iVal[0], rdata.iVal[1], rdata.dVal);
}
查看更多
爷、活的狠高调
6楼-- · 2019-04-08 14:15

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

查看更多
登录 后发表回答