我试图了解 C++11 是如何扩展联合的。改变的一件事是现在能够将非静态数据成员与重要的特殊成员函数一起使用。来自 cppreference.com
如果联合体包含非静态数据成员和非平凡的特殊成员函数(默认构造函数、复制/移动构造函数、复制/移动赋值或析构函数),则该函数在联合体中默认被删除,并且需要由程序员明确定义。 最多一个数据成员可以有一个默认成员初始值设定项。
我正在尝试以下代码:
struct X
{
~X() {};
};
union U
{
X x;
~U() {};
};
int main()
{
U s1{}; // works, probably aggregate initialization
U s2; // DOES NOT compile, why?
}
这里
X
(用作联合体的数据成员)有一个用户提供的析构函数,因此默认情况下删除联合体的析构函数。因此我明确提供了一个。但是,代码无法编译,并出现错误
注意:“U::U()”被隐式删除,因为默认定义格式不正确:
如果删除最后一行,代码就会编译
U s2;
。
问题这是怎么回事?为什么
U s1{};
可以编译,但 U s2;
不能编译?联合体的默认构造函数是否被标记为已删除(如果是这样,为什么?!),并且在第一种情况下我们只是聚合初始化?请注意,如果我提供 U(){}; // not U() = default;
,代码就会编译(但如果我只提供 X
的 ctor,则不会)。
编辑
深入研究标准(N4527)后:
工会:9.5/2 [class.union]
[注意:如果联合体的任何非静态数据成员具有重要的默认构造函数 (12.1)、复制构造函数 (12.8)、移动构造函数 (12.8)、复制赋值运算符 (12.8)、移动赋值运算符 (12.8)或析构函数 (12.4),联合体的相应成员函数必须由用户提供,否则它将为联合体隐式删除 (8.4.3)。 ——尾注]
这似乎是一个 gcc bug(现已报告here)。该代码在 clang 和 gcc 4.8.2 或更早版本上编译,在 gcc4.9 及更高版本上编译(感谢 @T.C. 指出)。
编译器:g++5.3,使用
-std=c++11
。
X 不是 pod 类型,因为它具有非平凡的析构函数,因此不可轻易复制。
U 也不是 Pod 类型。
U s2;
尝试调用被删除的默认构造函数所以错误
U s1 {};
使用成员明智的初始化并且不调用任何构造函数
在与非 POD 成员的联合中,联合的默认构造函数被删除,因为它会调用成员的默认构造函数,即编译器不知道调用默认构造函数的成员
union XX{
string m1;
vector <int> m2;
}
XX 的默认构造函数无法调用 m1 AND m2 的默认构造函数,因此被删除。
cppreference 引用不清楚。发生的情况是,如果联合体的ANY成员定义了ANY这些非平凡的特殊成员函数,那么它们中的ALL将在联合体中默认删除。
因此,由于
X
有一个重要的析构函数,因此 U
默认构造函数被删除。