联合中的大括号或相等Initializer

brace-or-equal-Initializer in unions

本文关键字:Initializer      更新时间:2023-10-16

相关:如何在Union 中初始化非POD成员

标准上说

联合中最多有一个非静态数据成员可以具有大括号或相等的初始值设定项。

但是

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;
}

打印CCD_ 1而不是期望的CCD_。

我认为这是一个编译器错误。是这样吗?

C++11[class.cctor]/5状态:

X默认构造函数是类X的构造函数,可以在没有参数的情况下调用。如果类X没有用户声明的构造函数,则没有参数的构造函数将隐式声明为默认构造函数(8.4)。隐式声明的默认构造函数是其类的inline public成员。类X的默认构造函数被定义为已删除,如果:
  • CCD_ 8是一个类并集类,它有一个带有非平凡默认构造函数的变体成员
  • 任何没有大括号或相等初始值设定项的非静态数据成员都是引用类型
  • 任何不带大括号或等号初始值设定项的const限定类型(或其数组)的非变体非静态数据成员都没有用户提供的默认构造函数
  • CCD_ 9是并集并且其所有变体成员都是常量限定类型(或其数组)
  • CCD_ 10是非并集类并且任何匿名并集成员的所有成员都是常量限定类型(或其数组)
  • 任何直接或虚拟基类,或没有大括号或相等初始值设定项的非静态数据成员,具有类类型4196960:01(或其数组),并且M没有默认构造函数或重载解析(13.3),因为应用于M的默认构造函数会导致歧义,或导致函数从默认默认构造函数中删除或无法访问,或
  • 任何直接或虚拟基类或非静态数据成员都有一个带析构函数的类型,该类型已从默认默认构造函数中删除或无法访问

如果默认构造函数不是用户提供的,并且如果:

  • 其类没有虚拟函数(10.3),也没有虚拟基类(10.1),并且
  • 其类的任何非静态数据成员都没有大括号或相等的初始值设定项,并且
  • 其类的所有直接基类都有琐碎的默认构造函数,并且
  • 对于类类型(或其数组)的类的所有非静态数据成员,每个此类都有一个琐碎的默认构造函数

否则,默认构造函数是非平凡的

由于OP中的结构体Point有一个非平凡的默认构造函数

Point() {}

包含类型为Point的成员的联合的默认默认构造函数应根据第一个项目符号定义为已删除

  • X是一个类似并集的类,它有一个带有非平凡默认构造函数的变体成员

导致OP中显示的程序格式错误。

然而,根据核心工作组问题1623:,如果工会成员有括号或相等的初始值设定项,委员会似乎认为这是一个缺陷

根据12.1[class.ctor]第5段,

类X的默认构造函数被定义为已删除,如果:

  • X是一个类似并集的类,它有一个带有非平凡默认构造函数的变体成员

  • 。。。

  • X是并集,其所有变体成员都是常量限定类型(或其数组),

  • X是一个非并集类,任何匿名并集成员的所有成员都是常量限定类型(或其数组),

  • 。。。

因为非静态数据成员初始值设定项的存在在道义上等同于mem初始值设定项,所以当联合成员具有非静态数据元素初始值设定时,可能应该修改这些规则,不要将生成的构造函数定义为已删除。(注意9.5【联合类】第2-3段和7.1.6.1【dcl.type.cv】第2段中的非规范性参考文献,如果该限制发生变化,也需要更新。)

如果联合的所有成员都具有const限定类型,那么向9.5〔class.union〕添加一个要求非静态数据成员初始值设定项或用户提供的构造函数也会很有帮助。

更一般地说,为什么仅仅因为成员有一个非平凡的默认构造函数,就将默认构造函数定义为已删除?联合本身不知道哪个成员是活动成员,默认构造不会初始化任何成员(假设没有大括号或相等的初始化器)。由联合的"所有者"来控制活动成员(如果有的话)的生存期,而要求用户提供的构造函数会迫使设计模式变得毫无意义。同样,为什么仅仅因为成员有一个非平凡的析构函数就将默认析构函数定义为已删除?如果只有当联合也有用户提供的构造函数时才适用,我会同意这个限制。

第1623期的状态为"起草",表明委员会认为该问题可能是一个缺陷-否则为什么允许工会成员使用大括号或相等的初始值设定项?-但尚未花时间确定决议的正确措辞。事实上,该段在当前的C++14草案N3936([class.cctor]/4)中基本相同,只是"任何直接或虚拟基类或非静态数据成员"的措辞处处被更简单的"任何潜在构建的子对象"所取代

尽管两个编译器的行为并不严格一致,但我认为Clang的行为符合标准的精神。GCC似乎被删除的默认构造函数和大括号或相等的初始值设定项的组合所混淆:

  • 在没有大括号或相等的初始值设定项的情况下,它确实将程序诊断为格式错误
  • 存在大括号或相等的初始值设定项并发出最大警告时,GCC 4.8.2根本不执行并集的初始化,甚至警告成员未初始化即被使用:

    main.cpp: In function 'int main()':
    main.cpp:17:39: warning: 'u.U::p.Point::y_' is used uninitialized in this function [-Wuninitialized]
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
    ^
    main.cpp:17:22: warning: 'u.U::p.Point::x_' is used uninitialized in this function [-Wuninitialized]
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
    ^
    

GCC可能应该符合标准并将程序诊断为格式错误,或者模拟clang的行为并从大括号或相等的初始值设定项生成正确的构造函数

相关文章: