Seb*_*ach 21 c++ g++ compiler-bug c++11
标准说
联合的至多一个非静态数据成员可以具有支撑或等于初始化器.
但
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;
}
Run Code Online (Sandbox Code Playgroud)
打印4196960:0而不是预期的1:2.
我认为这是一个编译器错误.是这样吗?
C++11 [class.ctor]/5 状态:
\n\n\n\n\n类的默认构造函数是可以在不带参数的情况下调用的
\n\nX类的构造函数。X如果 class 没有用户声明的构造函数X,则没有参数的构造函数将隐式声明为默认构造函数 (8.4)。隐式声明的默认构造函数是inline public其类的成员。如果满足以下条件,则类的默认默认构造函数X被定义为已删除:\n
\n\n- \n
X是一个类似联合的类,它有一个带有非平凡默认构造函数的变体成员,- 任何没有大括号或等于初始化器的非静态数据成员都是引用类型,
\n- 任何没有大括号或等于初始化器的 const 限定类型(或其数组)的非变体非静态数据成员不具有用户提供的默认构造函数,
\n- \n
X是一个联合体,其所有变体成员都是 const 限定类型(或其数组),- \n
X是一个非联合类,并且任何匿名联合成员的所有成员都是 const 限定类型(或其数组),- 任何直接或虚拟基类,或没有大括号或等于初始值设定项的非静态数据成员,都具有类类型
\nM(或其数组),并且M没有应用到的默认构造函数或重载解析(13.3)M\xe2\x80的默认构造函数或重载解析 (13.3) \x99s 默认构造函数会导致歧义或导致函数被删除或无法从默认默认构造函数访问,或者- 任何直接或虚拟基类或非静态数据成员都具有带有析构函数的类型,该析构函数已从默认的默认构造函数中删除或无法访问。
\n如果默认构造函数不是用户提供的并且满足以下条件,则它是微不足道的:
\n\n\n
\n\n- 它的类没有虚函数(10.3),也没有虚基类(10.1),并且
\n- 其类的非静态数据成员没有大括号或等于初始化程序,并且
\n- 其类的所有直接基类都有简单的默认构造函数,并且
\n- 对于其类中属于类类型(或其数组)的所有非静态数据成员,每个此类都有一个简单的默认构造函数。
\n否则,默认构造函数是不平凡的。
\n
由于结构Point由于OP中的
Point() {}\nRun Code Online (Sandbox Code Playgroud)\n\n包含类型成员的联合的默认默认构造函数Point 应该根据第一个项目符号定义为已删除:
\n\n\n\n
\n- \n
X是一个类似联合的类,它有一个带有非平凡默认构造函数的变体成员
导致OP中提出的程序格式不正确。
\n\n然而,根据核心工作组问题 1623,委员会似乎认为在工会成员具有大括号或等于初始化器的情况下这是一个缺陷的情况下这是一个缺陷:
\n\n\n\n\n根据 12.1 [class.ctor] 第 5 段,
\n\n\n\n\n如果满足以下条件,则类 X 的默认构造函数被定义为已删除:
\n\n\n
\n- \n
X是一个类似联合的类,它有一个带有非平凡默认构造函数的变体成员,- \n
...
- \n
X是一个联合体,其所有变体成员都是 const 限定类型(或其数组),- \n
X是一个非联合类,并且任何匿名联合成员的所有成员都是 const 限定类型(或其数组),- \n
...
因为非静态数据成员初始值设定项的存在在道德上等同于内存初始值设定项,所以可能应该修改这些规则,以便在联合成员具有非静态数据成员初始值设定项时不将生成的构造函数定义为已删除。(请注意 9.5 [class.union] 第 2-3 段和 7.1.6.1 [dcl.type.cv] 第 2 段中的非规范性引用,如果此限制发生更改,也需要更新。)
\n\n如果联合体的所有成员都具有 const 限定类型,则向 9.5 [class.union] 添加要求非静态数据成员初始值设定项或用户提供的构造函数也会很有帮助。
\n\n更一般地说,为什么仅仅因为成员有一个不平凡的默认构造函数就将默认构造函数定义为已删除?联合本身不知道哪个成员是活动成员,并且默认构造不会初始化任何成员(假设没有大括号或等于初始化程序)。由联合体的 \xe2\x80\x9cowner\xe2\x80\x9d 来控制活动成员(如果有)的生命周期,并且需要用户提供的构造函数会强制采用一种不\xe2\x80\x9d 的设计模式合理。同样,为什么仅仅因为成员有一个不平凡的析构函数就将默认析构函数定义为已删除?如果仅当联合体也有用户提供的构造函数时才应用此限制,我会同意此限制。
\n
问题 1623 的状态为“起草”,表明委员会认为该问题可能是一个缺陷 - 为什么要允许工会成员使用大括号或等于初始化器?- 但尚未花时间确定决议的正确措辞。事实上,该段落与当前的 C++14 草案 N3936 ([class.ctor]/4) 基本相同,只是“任何直接或虚拟基类或非静态数据成员”这一措辞到处都被替换为更简单的“任何潜在构造的子对象”。
\n\n尽管两个编译器的行为并不严格一致,但我认为 Clang 的行为符合标准的精神。看起来 GCC 对删除的默认构造函数和大括号或等于初始化器的组合感到困惑:
\n\n当存在大括号或等于初始化器和最大警告时,GCC 4.8.2 根本不执行联合的初始化,甚至警告成员未初始化:
\n\nmain.cpp: In function \'int main()\':\nmain.cpp:17:39: warning: \'u.U::p.Point::y_\' is used uninitialized in this function [-Wuninitialized]\n std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;\n ^\nmain.cpp:17:22: warning: \'u.U::p.Point::x_\' is used uninitialized in this function [-Wuninitialized]\n std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;\n ^\nRun Code Online (Sandbox Code Playgroud)GCC 可能应该符合标准并将程序诊断为格式错误,或者模拟 clang 的行为并从大括号或等于初始化程序生成正确的构造函数。
\n