C++11 空列表联合的初始化 - 是否保证初始化联合的全长?
C++11 empty list Initialization of a union - is it guaranteed to initialize the full length of the union?
在 C++11 中,我有以下联合:
union SomeData
{
std::uint8_t Byte;
std::uint16_t Word;
std::uint32_t DWord;
unsigned char String[128];
};
如果我这样初始化联合;
SomeData data {};
是否保证联合全部内容"zero'd"出来?换一种方式;是联合的空列表初始化器在功能上等同于将联合记忆设置为零?:
memset(&data, 0, sizeof(data));
我尤其关心字符串数据。我想确保字符串的整个长度都包含零。它似乎在我当前的编译器中工作,但规范的语言是否保证这始终为真?
如果不是:是否有更好的方法将并集的全长初始化为零?
不,不能保证整个联合会被归零。只有联合的第一个声明的成员,加上任何填充,保证被归零(下面的证明)。
因此,要确保联合对象的整个内存区域都归零,您有以下选择:
- 对成员进行排序,使最大的成员排在第一位,从而将那个清零。
- 使用
std::memset
或同等功能。为了防止不小心忘记这一点,你当然可以给 SomeData
一个默认的构造函数来调用它。
引用 C++11:
8.5.4 [dcl.init.list]/3
List-initialization of an object or reference of type T
is defined as follows:
- If the initializer list has no elements and
T
is a class type with a default constructor, the object is
value-initialized.
8.5 [dcl.init]/7
To value-initialize an object of type T
means:
- if
T
is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T
is called (and the initialization is ill-formed if T
has no accessible default
constructor);
- if
T
is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T
’s implicitly-declared default constructor is non-trivial, that constructor is
called.
- ...
- otherwise, the object is zero-initialized.
8.5 [dcl.init]/5:
To zero-initialize an object or reference of type T
means:
...
- if
T
is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero-initialized
and padding is initialized to zero bits;
从这些引用可以看出,使用{}
初始化data
会导致对象被值初始化(因为SomeData
是一个class类型使用默认构造函数)。
在没有用户提供的默认构造函数(SomeData
是)的情况下对联合进行值初始化意味着对其进行零初始化。
最后,零初始化联合意味着零初始化它的第一个非静态命名数据成员。
整个联盟将被清零。更确切地说,联合的第一个成员将被默认初始化,联合中的所有剩余字节将被设置为 0,如 padding.
参考文献(强调我的):
8.5 Initializers [dcl.init]
...
5 To zero-initialize an object or reference of type T means:
...
— if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero initialized
and padding is initialized to zero bits;
这意味着联合的第一个成员(此处 std::uint8_t Byte;
)将被初始化为 0,并且联合中的所有其他字节都将被设置为 0,因为它们是填充字节。
但是小心。正如 Angew 所说,“填充”在标准 中的定义非常少,C 编译器可以解释联合中的填充字节只是跟在最大成员之后的字节。我真的会觉得这很奇怪,因为专门记录了兼容性更改,并且以前的版本 (C) 首先将所有内容初始化为 0,然后进行特定的初始化。但是一个新的实现者可能不知道它...
TL/DR:我真的认为标准的意图是在 OP 的示例中联合中的所有字节都设置为 0,但是对于关键任务程序,我肯定会添加一个显式的 0 构造函数...
在 C++11 中,我有以下联合:
union SomeData
{
std::uint8_t Byte;
std::uint16_t Word;
std::uint32_t DWord;
unsigned char String[128];
};
如果我这样初始化联合;
SomeData data {};
是否保证联合全部内容"zero'd"出来?换一种方式;是联合的空列表初始化器在功能上等同于将联合记忆设置为零?:
memset(&data, 0, sizeof(data));
我尤其关心字符串数据。我想确保字符串的整个长度都包含零。它似乎在我当前的编译器中工作,但规范的语言是否保证这始终为真?
如果不是:是否有更好的方法将并集的全长初始化为零?
不,不能保证整个联合会被归零。只有联合的第一个声明的成员,加上任何填充,保证被归零(下面的证明)。
因此,要确保联合对象的整个内存区域都归零,您有以下选择:
- 对成员进行排序,使最大的成员排在第一位,从而将那个清零。
- 使用
std::memset
或同等功能。为了防止不小心忘记这一点,你当然可以给SomeData
一个默认的构造函数来调用它。
引用 C++11:
8.5.4 [dcl.init.list]/3
List-initialization of an object or reference of type
T
is defined as follows:
- If the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.
8.5 [dcl.init]/7
To value-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);- if
T
is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, ifT
’s implicitly-declared default constructor is non-trivial, that constructor is called.- ...
- otherwise, the object is zero-initialized.
8.5 [dcl.init]/5:
To zero-initialize an object or reference of type
T
means:...
- if
T
is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero-initialized and padding is initialized to zero bits;
从这些引用可以看出,使用{}
初始化data
会导致对象被值初始化(因为SomeData
是一个class类型使用默认构造函数)。
在没有用户提供的默认构造函数(SomeData
是)的情况下对联合进行值初始化意味着对其进行零初始化。
最后,零初始化联合意味着零初始化它的第一个非静态命名数据成员。
整个联盟将被清零。更确切地说,联合的第一个成员将被默认初始化,联合中的所有剩余字节将被设置为 0,如 padding.
参考文献(强调我的):
8.5 Initializers [dcl.init]
...5 To zero-initialize an object or reference of type T means:
...
— if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero initialized and padding is initialized to zero bits;
这意味着联合的第一个成员(此处 std::uint8_t Byte;
)将被初始化为 0,并且联合中的所有其他字节都将被设置为 0,因为它们是填充字节。
但是小心。正如 Angew 所说,“填充”在标准 中的定义非常少,C 编译器可以解释联合中的填充字节只是跟在最大成员之后的字节。我真的会觉得这很奇怪,因为专门记录了兼容性更改,并且以前的版本 (C) 首先将所有内容初始化为 0,然后进行特定的初始化。但是一个新的实现者可能不知道它...
TL/DR:我真的认为标准的意图是在 OP 的示例中联合中的所有字节都设置为 0,但是对于关键任务程序,我肯定会添加一个显式的 0 构造函数...