联合的活动成员,统一初始化和构造函数

时间:2015-07-13 16:05:09

标签: c++ constructor unions uniform-initialization

正如(Working Draft of) C++ Standard所说:

  

9.5.1 [class.union]

     

在联合中,最多其中一个非静态数据成员可以随时处于活动状态,也就是说,最多可以存储一个非静态数据成员的值在任何时候的工会。 [...] union的大小足以包含其中最大的非静态数据成员。每个非静态数据成员都被分配,就好像它是结构的唯一成员一样。 union对象的所有非静态数据成员都具有相同的地址。

但是我不知道如何识别哪个是工会的活跃成员而且我没有用到足够深入标准来找到标准所说的关于它的内容,我试图弄清楚它是如何活跃的成员已设置,但我已经找到了如何交换:

  

9.5.4 [class.union]

     

[注意:通常,必须使用显式析构函数调用和放置新运算符来更改联合的活动成员。 - 结束注释] [示例:考虑具有u类型的非静态数据成员m的union type U的对象Mn类型的N。如果M具有非平凡的析构函数并且N具有非平凡的构造函数(例如,如果它们声明或继承虚函数),则u的活动成员可以安全地从{{1使用析构函数和放置new运算符到m,如下所示:

n
     

- 结束示例]

所以我的猜测是联盟的活跃成员是首先签名,使用,构建或放置新的成员;但是,对于统一初始化,这会变得有点棘手,请考虑以下代码:

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'; 是否正在阅读工会的活跃成员?下面的代码怎么样?

std::cout

使用上面的行我们可以初始化嵌套的匿名结构或数组,如果我只提供一个整数,我可以初始化Foo f{0,1,2,3}; // uniform initialization std::cout << f.a << f.b << f.c << f.d << '\n'; Foo::aFoo::array ...哪一个会成为活跃会员吗?

Foo::integer

我想在上述所有案例中,活跃成员都是无形结构,但我不确定。

如果我想激活一个或另一个联盟成员,我应该提供一个激活它的构造函数吗?

Foo f{0}; // uniform initialization
std::cout << f.integer << '\n';

我几乎可以肯定#1和#3会将匿名结构和整数标记为活动联合,但我不知道#2,因为当我们到达构造函数的主体时,成员已经建造!那么我们是否在一个不活跃的工会成员上调用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; };

问题:

  • 如果使用以下统一初始化构造std::copy,则它们是Foo的活动联合成员:
    • Foo{};
    • Foo{1,2,3,4};
    • Foo{1};
  • Bar的#2构造函数中,Bar::array是活动的联盟成员吗?
  • 我可以在标准中哪些地方了解哪个是活跃的工会会员以及如何在没有新的位置的情况下设置它?

2 个答案:

答案 0 :(得分:3)

您对工会活跃成员的严格定义的关注由标准化委员会(至少部分)成员共享 - 请参阅最新说明(2015年5月) )在active issue 1116

的描述中
  

我们永远不会说联盟的活跃成员是什么,如何改变,等等。 [...]

我认为我们可以期待在工作草案的未来版本中进行某种澄清。该说明还表明,我们迄今为止所做的最好的是您在问题中引用的段落中的注释,[9.5p4]。

话虽如此,让我们看看你的其他问题。

首先,标准C ++中没有匿名结构(只有匿名联合);如果使用合理严格的选项编译,struct {char a,b,c,d;};将给出警告(例如,对于Clang和GCC,-std=c++1z -Wall -Wextra -pedantic)。展望未来,我假设我们有一个类似struct { char a, b, c, d; } s;的声明,其他所有内容都会相应调整。

第一个示例中隐式默认的默认构造函数根据[12.6.2p9.2]不执行任何初始化:

  

在非委托构造函数中,如果给定可能构造的   子对象不是由 mem-initializer-id 指定的(包括   因为构造函数没有 mem-initializer-list 的情况   没有 ctor-initializer ),然后

     

(9.1) - 如果实体是一个非静态数据成员,其中包含大括号或等于初始值

     
    

(9.1.1) - 构造函数的类是一个联合(9.5),该联合的其他变体成员没有被 mem-initializer-id
指定     (9.1.2) - 构造函数的类不是联合,并且,如果实体是匿名联合的成员,则 mem-initializer-id 不会指定该联合的其他成员,

  
     

按照8.5中的规定初始化实体;

     

(9.2) - 否则,如果实体是匿名联合或变体成员(9.5),则不执行初始化;

     

(9.3) - 否则,实体默认初始化(8.5)。

我想我们可以说f在默认构造函数执行完毕后没有活动成员,但我不知道有任何标准措辞可以清楚地表明这一点。在实践中可以说的是,尝试阅读任何f成员的价值是没有意义的,因为他们不确定。

在下一个示例中,您正在使用聚合初始化,根据[8.5.1p16]为工会合理定义:

  

当使用大括号括起初始化器初始化union时,   大括号只能包含第一个 initializer-clause   联盟的非静态数据成员。 [示例:

union u { int a; const char* b; }; 
u a = { 1 }; 
u b = a; 
u c = 1;               // error 
u d = { 0, "asdf" };   // error 
u e = { "asdf" };      // error 
     

- 结束示例]

与[em> brace elision 一起用于初始化嵌套结构,如[8.5.1p12]中所指定的那样,使结构成为活动成员。它也回答了您的下一个问题:您只能使用该语法初始化第一个union成员。

你的下一个问题:

  

如果我想激活一个或另一个联盟成员,我应该提供一个激活它的构造函数吗?

是,或者根据上面引用的[12.6.2p9.1.1],只为一个成员提供大括号或等于初始值;像这样的东西:

union Foo
{
    struct { char a, b, c, d; } s;
    char array[4];
    int integer = 7;
};

Foo f;

在上述之后,活跃成员将是integer。以上所有内容也应该回答你关于#2的问题(当我们到达构造函数的主体时,构件尚未构造 - #2也没问题。)

总结,Foo{}Foo{1}都执行聚合初始化;它们分别被解释为Foo{{}}Foo0(因为括号省略),并初始化结构;根据[8.5.1p7],第一个将所有结构成员设置为1,第二个成员将第一个成员设置为0,其余成员设置为{{1}}。

所有标准报价均来自当前工作草案N4527

论文N4430,它处理了一些相关问题,但尚未集成到工作草案中,为活跃成员提供了一个定义:

  

在联合中,如果非静态数据成员的名称指的是其生命周期已开始但尚未结束的对象([basic.life]),则该成员处于活动状态。

这有效地将降价转移到了[3.8]中的生命定义,它也有一些问题,包括前面提到的issue 1116,所以我认为我们必须等待几个需要解决此类问题才能获得完整一致的定义。目前生命周期的定义似乎还没有准备好。

答案 1 :(得分:0)

活动成员是您写入的最后一个成员。就这么简单。

该术语不是由C ++定义的,因为它是由英语定义的。