为什么匿名联合不能包含具有非平凡构造函数/析构函数的成员?

时间:2010-12-09 10:09:44

标签: c++ membership unions

我可能错了,但我发现的基本解释是联盟无法初始化,因为它不知道要调用哪个成员的构造函数。编译器无法自动为union生成构造函数。

为什么不允许用户定义联合构造函数?这将删除所述问题并允许存在具有非平凡构造函数/析构函数的联合成员。

另外,为什么工会成员不能任何自定义构造函数?前面的解释不适用于自定义构造函数。

更新1:

示例:

struct SQuaternion
{
    union
    {
        S3DVector Axis;
        struct
        {
            float X;
            float Y;
            float Z;
        };
    };
    float W;
};

注意:这里的问题似乎是联盟是匿名的。因此,如何命名联盟的构造函数?似乎不可能这样做,仅仅因为它没有名字,没有其他原因。如果这是一个简单的词汇问题,那将是一个可怕的原因......

更新2: 只需将有问题的成员包装在一个封闭的匿名结构中,错误就会消失。我想这是一个与匿名联盟最接近的事情。它不再是一个问题的事实似乎仍然很奇怪......

5 个答案:

答案 0 :(得分:13)

更大的原因是:工会如何知道要调用哪个析构函数。语言本身不会跟踪哪个成员在联合中处于活动状态。

似乎C ++ 0x将允许联合中的非平凡类型,在这种情况下,您将被迫实现自己的构造函数和析构函数。 (后者在proposal中有点不清楚,似乎union析构函数不会调用任何成员析构函数,并且必须手动调用正确析构函数的析构函数。)

答案 1 :(得分:2)

可能有可能定义这样的东西,但这会打开这样一种蠕虫,它几乎肯定不值得:

  1. 要构建哪个对象?
  2. 如果定义了一个union构造函数,它如何构造一个对象与另一个对象?
  3. 您是否必须为每个非pod成员定义一个构造函数?
  4. 将不同对象分配给当前分配的对象的语法是什么?
  5. 您是否允许在另一个对象的上下文中解释一个对象?目前, 允许加入工会,这是一个非常常见的用例(union { char c[4]; int n; } u; u.n=1234; cout << u.c[1];)。
  6. 编译器如何知道要调用哪个析构函数?
  7. 编译器如何知道在复制联合时要调用哪个拷贝构造函数?
  8. (我有什么遗漏了吗?)

    我怀疑它只是进入了太难的篮子里。

答案 2 :(得分:1)

我认为你是对的,C ++中的工会功能不足。它们几乎是来自C的联合的直接副本,这意味着它们不能作为用于C ++ 的变体类型。

C ++中的union没有简单的方法来表示正确的变体类型。如果合法,请考虑以下代码:

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

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

在存储~string之前,X的任何构造函数或析构函数都不会确保最后一行中的赋值调用23。对于编译器来说,联合必须包含某种类型的指示器存储的类型。这就是为什么一切都必须是POD。我不知道命名与未命名联盟之间存在差异的原因,但这适用于两者。

如果所有成员都是POD,C ++联盟可能被定义为C联盟,但是要包含这些额外信息,并在正确的时间调用正确的析构函数,如果任何成员是非POD。但这不是你提出的简单改变。

通过编写一个具有表示当前存储类型的值的类,然后构建器,复制赋值运算符和析构函数(如果允许的话),您可能会有点费力地编写变量类型

使用char数组进行存储,使用new作为构造/赋值,并在析构函数中直接调用正确的析构函数。

请注意对齐问题 - 您需要确保原始存储空间与您放置的任何类型都充分对齐。一种方法是动态分配它。另一种方法是将您的char数组放入一个具有最大对齐要求的内置类型的联合(如果您不知道:所有这些)。

与您想要的联合使用的唯一不同之处在于,您必须提供访问者,而不是公共数据成员int afloat bstring c。返回一个代理对象(可能是对象本身的引用),它能够正确分配,这意味着首先调用旧类型的析构函数。然后,您可以写x.i() = 23而不是x.i = 23

或者,您可以使用Boost.Variant

答案 3 :(得分:0)

这段代码似乎对我很好:

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);
}

答案 4 :(得分:0)

您已经听说过 - 成员的非平凡构造函数表明该对象旨在封装其数据内容,并且工会会删除该封装。将简单类型的联合放入类中是添加封装的好方法,并确保以合理,安全的方式使用联合内容。

拥有成员的工会:从9.5

  

union可以具有成员函数(包括构造函数和析构函数),但不具有虚函数(10.3)。工会不得有基类。联合不应该用作基类。具有非平凡构造函数(12.1)的类的对象,非平凡的复制构造函数(12.8),非平凡的析构函数(12.4)或非平凡的复制赋值运算符(13.5.3,12.8)不能是联合的成员,也不能是这类对象的数组