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

3
在下面的代码片段中,当方法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
这组编译器错误让我大吃一惊。在实际代码中,我有更多属性和函数。因此,我开始查找我在哪里放了bloody operator=()和constructor,但是我没有在struct Data中写它们。
我知道如果将Data :: setValues设置为非虚拟,则我的问题就会消失。但是为什么结构体Data有一个构造函数?C ++中什么时候会有结构体构造函数?为什么当我把virtual void Data :: setValues(int,int)变成非虚拟时,编译错误会消失?

当你说“我收到编译器错误,因为我从未打算将Data :: setValue()设置为虚拟函数”时,你的意思是什么?编译器错误与联合中的值有关,因此不清楚你对setValue()的虚拟性的意图与此有何关系。 - Chad
@chad 当我将virtual void Data::setValues(int, int)声明为void Data::setValues(int, int)时,编译器错误消失了。这里存在一些显著的差异,这就是我的问题所在。 - hetepeperfan
1
如果以下的答案不能满足您,这里有一个更实际的方法:考虑许多实现中都有的“vtable”指针。这个指针在构造期间被设置,这使得相应的对象不适合存储在联合体中,因为只有那些在构造期间不需要任何初始化的对象才能被放置在彼此之上。 - Ulrich Eckhardt
5个回答

3
除了成员默认访问权限不同,C++中的structclass是相同的。除非你小心编程(并使用C++11),否则你不能在union中拥有classstruct成员,因为这样会存在歧义,不清楚如何构造联合体的成员。这就是你的编译器告诉你的。

这并不是真的,只要它是一个平凡的结构,你可以在联合中拥有结构。 - henryyao

2

您可能需要查看这个帖子:在C++中的结构体构造函数?

简而言之,与类一样,结构体也有默认构造函数,您可以重载并根据需要进行定义。


1

在C++中,结构体何时具有构造函数

始终具有。

错误信息有点混淆,但它是在说您不能在那里有一个类。

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

每个类都有构造函数和赋值运算符,无论是用户提供的还是不是。


1
编译器的错误信息有点误导人。从概念语言的角度来看,无论函数是否是虚拟的,您的类都将具有构造函数。
编译器显然是一个 C++11 之前的版本(或者在 C++11 之前的模式下工作)。它不喜欢的构造函数是非平凡的事实。特别是的构造函数的非平凡性使得编译器说是一个“带构造函数”的类。
在这种特殊情况下,一旦在类中引入至少一个虚函数,构造函数就变得非平凡。
非平凡的构造函数是一个必须执行某些操作的构造函数(在您的情况下 - 初始化支持多态的每个对象的家庭信息)。也就是说,它是一个必须在生成的代码中物理存在的构造函数。同时,一个平凡的构造函数仅在概念上存在,但不产生任何代码。编译器在将类分为“带构造函数”和“不带构造函数”的类时,指的是这种物理上的区别。

我确实编译了,但没有使用--std=c++0x标志。 - hetepeperfan

1
你引用的错误消息具有误导性,所以毫无疑问你想知道为什么使函数非虚拟可以解决问题。
首先,在C ++中的结构和类具有构造函数和复制赋值运算符。如果您没有自己创建它们,系统会自动创建它们。(您可以删除这些默认版本)。在您的示例中有一个Data的自动生成的构造函数,还有自动生成的赋值运算符。
现在为什么错误消息具有误导性呢?因为它们是不正确的。在C ++中,您可以具有具有构造函数或赋值运算符的联合成员。问题开始于那些成员函数不是微不足道的时候。在C++11之前,无法具有带有非微不足道构造函数的联合成员。C ++11改变了这一点,但是如果没有编写其他函数,就不能使用这种联合。
具有虚函数的结构/类具有非微不足道的成员函数作为构造函数和赋值运算符(因为需要管理隐藏数据成员)。这就是为什么当您使该函数非虚拟时,错误消息消失了-然后成员函数变得微不足道,您的结构就可以作为联合成员使用,而不会出现问题的原因。

<nitpick>C++没有结构体(或简称“structs”)。只有类。可以使用“struct”关键字定义类,但这仅用于兼容性,并不使它们成为“structs”。</nitpick>(我认为这是向C程序员解释C++如何工作的更有效的方法。)值得一提的是,这并不总是这样:在1987年左右之前,C++既有类又有结构体,其中结构体的工作方式与C中相同。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接