Pap*_*ter 6 c++ constructor unions uniform-initialization
正如C++标准(工作草案)所说:
9.5.1 [class.union]
在并集中,至多一个非静态数据成员可以在任何时间处于活动状态,也就是说,任何时候最多一个非静态数据成员的值都可以存储在并集中.[...] union的大小足以包含其中最大的非静态数据成员.每个非静态数据成员都被分配,就好像它是结构的唯一成员一样.union对象的所有非静态数据成员都具有相同的地址.
但我不知道如何识别哪个是工会的活跃成员,而且我没有用到足够深入标准来找到标准所说的内容,我试图弄清楚活动成员是如何设置的但我发现它是如何交换的:
9.5.4 [class.union]
[ 注意:通常,必须使用显式析构函数调用和放置新运算符来更改联合的活动成员.- 注完 ] [ 实施例:考虑的对象
u
的union type U
类型的具有非静态数据成员米M
和n
类型的N
.如果M
有一个非平凡的析构函数并且N
有一个非平凡的构造函数(例如,如果它们声明或继承虚函数),则可以安全地将u的活动成员切换m
为n
使用析构函数和placement new运算符,如下所示:Run Code Online (Sandbox Code Playgroud)u.m.~M(); new (&u.n) N;
- 结束例子 ]
所以我的猜测是,工会的活跃成员是首先签署,使用,构建或放置新的成员; 但是这对于统一初始化变得有点棘手,请考虑以下代码:
union Foo
{
struct {char a,b,c,d;};
char array[4];
int integer;
};
Foo f; // default ctor
std::cout << f.a << f.b << f.c << f.d << '\n';
Run Code Online (Sandbox Code Playgroud)
哪个是上面代码的联合的活跃成员?是std::cout
从工会的活跃成员读?下面的代码怎么样?
Foo f{0,1,2,3}; // uniform initialization
std::cout << f.a << f.b << f.c << f.d << '\n';
Run Code Online (Sandbox Code Playgroud)
使用上面的行我们可以初始化嵌套的匿名结构或数组,如果我只提供一个我可以初始化的整数Foo::a
,Foo::array
或者Foo::integer
......哪一个是活动成员?
Foo f{0}; // uniform initialization
std::cout << f.integer << '\n';
Run Code Online (Sandbox Code Playgroud)
我想在上述所有案例中,活跃成员都是无关紧要的结构,但我不确定.
如果我想激活一个或另一个联盟成员,我应该提供一个激活它的构造函数吗?
union Bar
{
// #1 Activate anonymous struct
Bar(char x, char y, char z, char t) : a(x),b(y),c(z),d(t) {}
// #2 Activate array
Bar(char (&a)[4]) { std::copy(std::begin(a), std::end(a), std::begin(array)); }
// #3 Activate integer
Bar(int i) : integer(i) {}
struct {char a,b,c,d;};
char array[4];
int integer;
};
Run Code Online (Sandbox Code Playgroud)
我几乎可以肯定#1和#3会将匿名结构和整数标记为活动联合,但我不知道#2,因为在我们到达构造函数体的那一刻,成员已经被构造了!那么我们是否呼吁std::copy
一个不活跃的工会成员?
问题:
Foo
如果使用以下统一初始化构造,则哪些是活动联合成员:
Foo{};
Foo{1,2,3,4};
Foo{1};
Bar
的Bar::array
是主动工会成员?您对联盟活跃成员缺乏严格定义的担忧得到了标准化委员会(至少部分)成员的关注 - 请参阅活跃问题 1116描述中的最新说明(日期为 2015 年 5 月) :
\n\n\n\n\n我们从不说工会的活跃成员是什么、如何改变等等。[...]
\n
我认为我们可以期待工作草案的未来版本中得到某种澄清。该注释还表明,我们迄今为止最好的注释是您在问题中引用的段落中的注释,[9.5p4]。
\n\n话虽这么说,让我们看看你的其他问题。
\n\n首先,标准C++中没有匿名结构(只有匿名联合);struct {char a,b,c,d;};
如果使用相当严格的选项进行编译(-std=c++1z -Wall -Wextra -pedantic
例如,对于 Clang 和 GCC),将会向您发出警告。展望未来,我假设我们有一个类似的声明struct { char a, b, c, d; } s;
,其他所有内容都会相应调整。
第一个示例中的隐式默认默认构造函数不会根据 [12.6.2p9.2] 执行任何初始化:
\n\n\n\n\n在非委托构造函数中,如果给定的潜在构造子对象不是由mem-initializer-id指定的(包括由于构造函数没有构造函数初始化器而没有mem-initializer-list 的情况) ), 然后
\n\n(9.1) - 如果实体是具有大括号或等于初始化程序的非静态数据成员,并且
\n\n\n\n\n(9.1.1) - 构造函数\xe2\x80\x99s 类是一个联合 (9.5),并且该联合的其他变体成员不是由 mem -initializer-id或
\n
\n 指定的 (9.1.2) - 构造函数\xe2\x80\x99s 类不是联合体,并且,如果该实体是匿名联合体的成员,则该联合体的其他成员都不会由 mem -initializer-id指定,该实体按照 8.5 的规定进行初始化;
\n\n(9.2) - 否则,如果实体是匿名联合体或变体成员 (9.5),则不执行初始化;
\n\n(9.3) - 否则,实体默认初始化 (8.5)。
\n
我想我们可以说f
在其默认构造函数完成执行后没有活动成员,但我不知道有任何标准措辞可以清楚地表明这一点。在实践中可以说的是,尝试读取任何f
成员的值是没有意义的,因为它们是不确定的。
在下一个示例中,您将使用聚合初始化,它根据 [8.5.1p16] 对联合进行了相当明确的定义:
\n\n\n\n\n当使用大括号括起来的初始值设定项初始化联合时,大括号应仅包含联合的第一个非静态数据成员的初始值设定项子句。[例子:
\n\nRun Code Online (Sandbox Code Playgroud)\n\nunion u { int a; const char* b; }; \nu a = { 1 }; \nu b = a; \nu c = 1; // error \nu d = { 0, "asdf" }; // error \nu e = { "asdf" }; // error \n
\xe2\x80\x94结束示例]
\n
这与[8.5.1p12]中指定的用于初始化嵌套结构的大括号省略一起使该结构成为活动成员。它也回答了您的下一个问题:您只能使用该语法初始化第一个联合成员。
\n\n你的下一个问题:
\n\n\n\n\n如果我想激活一个或另一个联合成员,我应该提供一个构造函数来激活它吗?
\n
是的,或者根据上面引用的 [12.6.2p9.1.1] 为一个成员使用大括号或等于初始化器;像这样的东西:
\n\nunion Foo\n{\n struct { char a, b, c, d; } s;\n char array[4];\n int integer = 7;\n};\n\nFoo f;\n
Run Code Online (Sandbox Code Playgroud)\n\n完成上述操作后,将成为活跃会员integer
。上述所有内容也应该回答您的问题#2
(当我们到达构造函数的主体时,成员尚未构造 -#2
也很好)。
总结一下,Foo{}
和都Foo{1}
执行聚合初始化;它们分别被解释为Foo{{}}
和Foo{{1}}
(因为大括号省略),并初始化结构体;根据[8.5.1p7],第一个将所有结构成员设置为0
,第二个将第一个成员设置为,1
其余成员设置为。0
所有标准引用均来自当前工作草案N4527。
\n\n论文N4430涉及一些相关问题,但尚未纳入工作草案,它提供了活跃成员的定义:
\n\n\n\n\n在联合中,如果非静态数据成员的名称引用其生命周期已开始且尚未结束的对象 ([basic.life]),则该非静态数据成员处于活动状态。
\n
这有效地将责任转嫁到[3.8]中的生命周期定义上,该定义也存在一些问题,包括前面提到的问题 1116,所以我认为我们必须等待几个此类问题得到解决才能解决有一个完整且一致的定义。目前生命周期的定义似乎还没有完全准备好。
\n