C++ 联合内的非平凡结构构造函数

Non trivial struct constructor inside a union in C++

在以下代码片段中,当方法 Data::setValue(int, int) 声明为虚拟时,我收到编译器错误:

struct Data{
    int ma;
    int mb;
    virtual void setValues(int a, int b){
        ma = a;
        mb = b;
    }
};

struct ThreadMessage {
    enum type {
        DATA
    };

    type msg_type;
    union content {
        Data d;
        int a;
    }content;
};

编译器(g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3)给我的错误是:

struct.cpp:19:14: error: member 'Data ThreadMessage::content::d' with constructor not allowed in union

struct.cpp:19:14: error: member 'Data ThreadMessage::content::d' with copy assignment operator not allowed in union

这组编译器错误让我完全措手不及。在实际代码中,我有更多的属性和函数。因此,我开始寻找该死的 operator=() 和构造函数的位置,但我没有将它们写在 struct Data.

我知道当我将 Data::setValue 设置为非虚拟时,我的问题就消失了。但是为什么 struct Data 有一个构造函数呢?什么时候结构在 C++ 中有构造函数?为什么当我使 virtual void Data::setValues(int, int) 非虚拟时编译错误消失了?

除了成员的默认访问权限外,structclass 在 C++ 中是相同的。

除非你仔细编程(并且有 C++11),否则你不能在 union 中有一个 class 或一个 struct 成员,因为这样会产生歧义这样的工会会员应该建起来。

这就是你的编译器告诉你的。

您可能想浏览此主题:Struct Constructor in C++?

Reader 的摘要版本是,与 类 一样,结构具有默认构造函数,您可以根据需要重载和定义它。

When exactly does a struct have a constructor in c++

总是。

错误消息有点令人困惑,但它是说你不能在那里有 class。

class 是使用 struct 关键字或 class 关键字定义的类型。

每个 class 都有一个构造函数和一个赋值运算符,无论它是否由用户提供。

编译器错误消息有点误导。从概念语言的角度来看,您的 class Data 在任何情况下都会有一个构造函数,无论该函数是否为虚函数。

编译器显然是 C++11 之前的编译器(或者在 C++11 之前的模式下工作)。而且它不喜欢 Data 的构造函数 非平凡 这一事实。正是 Data 的构造函数的 非平凡性 使编译器说 Data 是 class "with constructor" .

在这种特殊情况下,一旦您在 class.

中引入至少一个虚函数,构造函数就会变得非常重要

一个非平凡的构造函数是一个必须做某事的构造函数(在你的例子中 - 初始化支持多态性的每个对象的家庭信息)。 IE。它是一个构造函数,必须物理存在于生成的代码中。同时,trivial 构造函数仅在概念上存在,但不产生任何代码。编译器在将 classes 分为 "with constructor" 和 "without constructor".

时指的是物理区别

您引用的错误消息具有误导性,因此您无疑想知道为什么将函数设为非虚函数可以解决问题。

首先 - C++ 中的结构和 类 具有构造函数和复制赋值运算符。如果您不自己创建它们,它们将为您创建。 (您可以删除那些默认版本)。在您的示例中,有一个自动生成的 Data 构造函数,还有一个自动生成的赋值运算符。

现在为什么错误消息具有误导性?因为它们不是真的。在 C++ 中,联合成员可以具有构造函数或赋值运算符。当这些成员函数不是微不足道的时候,问题就开始了。在 C++11 之前,不可能有一个具有非平凡构造函数的联合成员。 C++11 改变了它,但仍然不写额外的函数就不能使用这样的联合。

具有虚函数的struct/class具有非平凡的成员函数作为构造函数和赋值运算符(因为需要管理隐藏数据成员)。这就是为什么当您使函数成为非虚函数时错误消失的原因 - 然后成员函数变得微不足道并且您的结构可以毫无问题地用作联合成员。