C++ 11空列表联合的初始化 - 是否保证初始化联合的全长?

BTo*_*TKD 10 c++ unions c++11 list-initialization

在C++ 11中,我有以下联合:

union SomeData
{
    std::uint8_t Byte;
    std::uint16_t Word;
    std::uint32_t DWord;
    unsigned char String[128];
};
Run Code Online (Sandbox Code Playgroud)

如果我这样初始化联盟;

SomeData data {};
Run Code Online (Sandbox Code Playgroud)

是否保证联盟的全部内容都将"归零"?换一种方式; 是一个联合的空列表初始化程序,在功能上等同于将联合设置为零?:

memset(&data, 0, sizeof(data));
Run Code Online (Sandbox Code Playgroud)

特别是,我关心字符串数据.我想确保字符串的整个长度包含零.它似乎在我当前的编译器中工作,但规范的语言是否保证这始终是真的?

如果不是:有没有更好的方法将联合的全长初始化为零?

Rei*_*ica 5

不,不能保证整个联盟都会被淘汰.只保证联合的第一个声明成员加上任何填充,保证归零(下面的证明).

因此,为了确保union对象的整个内存区域归零,您有以下选项:

  • 对成员进行排序,使得最大的成员是第一个,因此将其归零.
  • 使用std::memset或等效功能.为了防止意外忘记,你当然可以给出SomeData一个默认的构造函数来调用它.

引用C++ 11:

8.5.4 [dcl.init.list]/3

对象或类型引用的列表初始化T定义如下:

  • 如果初始化列表没有元素并且T是具有默认构造函数的类类型,则对象将进行值初始化.

8.5 [dcl.init]/7

值初始化类型的对象T意味着:

  • if T是一个(可能是cv限定的)类类型(第9节)和一个用户提供的构造函数(12.1),然后T调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • 如果T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则该对象是零初始化的,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数.
  • ...
  • 否则,该对象被零初始化.

8.5 [dcl.init]/5:

零初始化的类型的对象或参考T指:

...

  • if T是一个(可能是cv限定的)联合类型,该对象的第一个非静态命名数据成员被零初始化,并且填充被初始化为零位;

从这些引号中,您可以看到使用{}初始化data将导致对象进行值初始化(因为SomeData是具有默认构造函数的类类型).

在没有用户提供的默认构造函数(即SomeData)的情况下初始化并集意味着对其进行零初始化.

最后,对联合进行零初始化意味着对其第一个非静态命名数据成员进行零初始化.

  • 这是[核心问题694](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#694).声明的意图是将整个联盟归零. (6认同)
  • @BTownTKD把最大的成员放在第一位? (3认同)
  • 我对*padding的理解初始化为零位*是union的剩余部分将被设置为0. (3认同)

Ser*_*sta 5

整个联盟将归零.更准确地说,union的第一个成员将被默认初始化,并且union中的所有剩余字节将被设置为0作为填充.

参考文献(强调我的):

8.5初始化程序[dcl.init]
...

5为零初始化T类型的对象或引用意味着:
...
- 如果T是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员初始化为零, 并且填充初始化为零位 ;

这意味着union的第一个成员(此处std::uint8_t Byte;)将初始化为0,并且union中的所有其他字节将设置为0,因为它们是填充字节.


要注意.正如Angew所说,"填充"在标准中非常缺乏, C编译器可以解释联合中的填充字节只是跟随最大成员的字节.我真的觉得很奇怪,因为兼容性更改是专门记录的,以前的版本(C)首先将所有内容初始化为0,然后进行特定的初始化.但是一个新的实施者无法意识到这一点......

TL/DR:我真的认为标准的意图是在OP的例子中联合中的所有字节都设置为0,但对于任务关键程序,我肯定会添加一个显式的0构造函数...