相关:How to initialize a non-POD member in Union
标准说
联合的最多一个非静态数据成员可能有一个大括号或等于初始值。
但是
struct Point {
Point() {}
Point(int x, int y): x_(x), y_(y) {}
int x_, y_;
};
union U {
int z;
double w;
Point p = Point(1,2);
};
#include <iostream>
int main () {
U u;
std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
}
打印4196960:0
而不是预期的1:2
。
我认为这是编译器错误。是这样吗?
答案 0 :(得分:2)
C ++ 11 [class.ctor] / 5陈述:
类
X
的默认构造函数是类X
的构造函数,可以在不带参数的情况下调用。如果类X
没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认值(8.4)。隐式声明的默认构造函数是其类的inline public
成员。如果出现以下情况,则将类X
的默认默认构造函数定义为已删除:
X
是一个类似联合的类,其变体成员具有非平凡的默认构造函数,- 任何没有 brace-or-equal-initializer 的非静态数据成员都是引用类型,
- 没有 brace-or-equal-initializer 的const-qualified类型(或其数组)的任何非变量非静态数据成员都没有用户提供的默认构造函数,
X
是一个联合,其所有变体成员都是const限定类型(或其数组),X
是一个非联合类,任何匿名联合成员的所有成员都是const限定类型(或其数组),- 任何直接或虚拟基类,或者没有 brace-or-equal-initializer 的非静态数据成员,具有类型
M
(或其数组)和{{ 1}}没有默认构造函数或重载解析(13.3),因为应用于M
的默认构造函数导致歧义或在默认构造函数中删除或无法访问的函数,或- 任何直接或虚拟基类或非静态数据成员都具有从默认默认构造函数中删除或无法访问的析构函数的类型。
如果默认构造函数不是用户提供的,并且如果:
,则默认构造函数是微不足道的
- 其类没有虚函数(10.3),没有虚基类(10.1)和
- 其类的非静态数据成员没有大括号或等于初始化,
- 其类的所有直接基类都有简单的默认构造函数和
- 对于类类的所有非静态数据成员(或其数组),每个这样的类都有一个普通的默认构造函数。
否则,默认构造函数是非平凡。
由于OP中的struct M
具有非平凡的默认构造函数,
Point
包含Point() {}
类型成员的联合的默认默认构造函数应根据第一个项目符号定义为已删除:
Point
是一个类似联合的类,其变体成员具有非平凡的默认构造函数
导致OP中呈现的程序格式不正确。
然而,委员会似乎认为这是一个缺陷,因为工会成员每core working group issue 1623都有支撑或平等初始化:
根据12.1 [class.ctor]第5段,
如果出现以下情况,则将类X的默认默认构造函数定义为已删除:
X
是一个类似联合的类,其变体成员具有非平凡的默认构造函数,...
X
是一个联合,其所有变体成员都是const限定类型(或其数组),
X
是一个非联合类,任何匿名联合成员的所有成员都是const限定类型(或其数组),...
因为非静态数据成员初始值设定项的存在是 mem-initializer 的道德等价物,所以应该修改这些规则,以便在工会成员拥有时将生成的构造函数定义为已删除非静态数据成员初始化程序。 (注意9.5 [class.union]第2-3和7.1.6.1段[dcl.type.cv]第2段中的非规范性引用,如果更改此限制,也需要更新。)
如果联合的所有成员都具有const限定类型,那么向9.5 [class.union]添加一个要求也是有帮助的,这需要非静态数据成员初始化程序或用户提供的构造函数。
更一般地说,为什么默认构造函数被定义为删除只是因为成员有一个非平凡的默认构造函数?联盟本身并不知道哪个成员是活动成员,并且默认构造不会初始化任何成员(假设没有大括号或等于初始值)。联合的“所有者”可以控制活动成员的生命周期(如果有的话),并且要求用户提供的构造函数强制设计模式没有意义。同样,为什么默认的析构函数被定义为删除只是因为一个成员有一个非平凡的析构函数?如果仅在union也有用户提供的构造函数时才应用,我同意这个限制。
问题1623具有状态&#34;起草,&#34;表明委员会认为问题可能是一个缺陷 - 为什么还要为工会成员提供支持或平等初始化? - 但尚未投入时间来确定解决方案的正确措辞。实际上,该段落在当前的C ++ 14草案N3936([class.ctor] / 4)中基本相同,除了措辞&#34;任何直接或虚拟基类或非静态数据成员&#34;无处不在被更简单的任何潜在构建的子对象所取代。&#34;
虽然两个编译器的行为并不严格一致,但我认为Clang的行为符合标准的精神。看来GCC被删除的默认构造函数和 brace-or-equal-initializer 的组合弄糊涂了:
brace-or-equal-initializer 存在且最大警告GCC 4.8.2根本不执行联合初始化,even warns that the members are used uninitialized:
X
GCC应该要么符合标准,要么将程序诊断为格式错误,要么模仿clang的行为,并从 brace-or-equal-initializer 生成一个合适的构造函数。