C++ 中的聚合初始化安全

Posted

技术标签:

【中文标题】C++ 中的聚合初始化安全【英文标题】:Aggregate Initialization Safety in C++ 【发布时间】:2011-09-11 04:29:54 【问题描述】:

假设我有以下结构:

struct sampleData

       int x;
       int y;
;

而在使用时,我想将sampleData类型的变量初始化为已知状态。

sampleData sample =  1, 2  

后来,我决定需要将额外的数据存储在我的sampleData 结构中,如下所示:

struct sampleData

       int x;
       int y;
       int z;
;

据我了解,从我的 pre-z 数据结构中遗留下来的两个字段初始化仍然是一个有效的语句,并将被编译,用默认值填充缺失的字段。

这种理解正确吗?我最近一直在 Ada 工作,它也允许聚合初始化,但会将类似的问题标记为编译错误。假设我对上面 C++ 代码的假设是正确的,是否有一种语言结构可以将缺少的初始化值识别为错误?

【问题讨论】:

【参考方案1】:

只有Aggregate Classes 支持以这种方式初始化变量。

如果您添加构造函数,那么问题就会消失,但是您需要稍微更改语法,并且您无法将 struct 存储在 union 中(除其他外)。

struct sampleData

    sampleData(int x, int y) : x(x), y(y) 
    int x;
    int y;
;

sampleData sample( 1, 2 );

添加z(并更改构造函数)会将sample( 1, 2 )标记为编译错误。

【讨论】:

+1。添加一个 (x,y,z) 构造函数,其中 Z 具有默认值,可以为您提供两全其美的效果。即使您对 Z 的默认值为零,它也有助于记录您在做什么, 不,只有聚合类才支持以这种方式初始化变量。所有聚合类都是 POD 类型,但并非所有 POD 类型都是聚合类,因此并非所有 POD 类型都支持聚合初始化。 @ildjam:感谢您的澄清。我已经更新了我的答案。 这也是我的答案(所以+1),但这样做有一个缺点。从技术上讲,这使得他的struct 不再是 POD。然而,在实践中,大多数编译器似乎都将其视为 POD,只要对它做的仅此而已。我称这样的结构为“pseudoPODs” @T.E.D. : 在 C++0x 中这样的东西有一个正式的名字——“标准布局类”。【参考方案2】:

是的,您离开初始化列表的任何元素都将被初始化为零(对于 POD 标量类型)或使用它们的默认构造函数(对于类)。

引用C标准中的相关语言here:

[6.7.8.21] 如果大括号括起来的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组,聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。

我确信有人比我在其中一个 C++ 规范中找到相应的语言更有动力...

请注意,这意味着 POD 标量元素的初始化就像您写“= 0”一样。这意味着它将正确地将指针初始化为 NULL 并浮动到 0.0,即使它们的表示不是全零字节。这也意味着它是递归工作的;如果您的结构包含结构,则内部结构也将正确初始化。

【讨论】:

出于使用结构的目的,我怀疑 C 标准与 C++ 规范相当接近 @Greg : 不,实际上——在 C++ 中,具有静态存储持续时间的对象如果是 POD 类型,则初始化为零,否则根据定义的方式进行初始化;另一方面,聚合初始化器中省略的数据成员始终是值初始化的。【参考方案3】:

作为 Nemo 对 C 标准的回答的后续,以下是 C++03 标准所说的:

§8.5.1/7:

如果列表中的初始化器少于聚合中的成员,则每个未显式初始化的成员都应进行值初始化。

§8.5/5:

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

如果T 是具有用户声明构造函数的类类型,则调用T 的默认构造函数(如果T 没有可访问的默认构造函数,则初始化格式错误); 如果T 是没有用户声明的构造函数的非联合类类型,则T 的每个非静态数据成员和基类组件都是值初始化的; 如果T是数组类型,那么每个元素都是值初始化的; 否则,对象被零初始化

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

如果T是标量类型,则将对象设置为0(零)的值转换为T; 如果T 是非联合类类型,则每个非静态数据成员和每个基类子对象都初始化为零; 如果T 是联合类型,则对象的第一个命名数据成员)为零初始化; 如果T是数组类型,每个元素都初始化为零; 如果T 是引用类型,则不执行初始化。

【讨论】:

@Nemo :我刚刚复制并粘贴了我的另一个已经格式化的答案。不涉及任何努力:-P【参考方案4】:

为什么不使用

sampleData sample =  x: 1, y:2  ;

?

但是您仍然会遇到z 被初始化为不可预测值的问题,因此最好定义一个将所有变量设置为明确定义的值的构造函数。

【讨论】:

因为问题被标记为c++ 而这不是 C++? sampleData sample = 1, 2 C++ 那么呢? 确实如此——它被称为“聚合初始化”。 这个答案现在是getting closer to being right as of C++20。

以上是关于C++ 中的聚合初始化安全的主要内容,如果未能解决你的问题,请参考以下文章

必须公开聚合字段构造函数才能在 C++ 中使用聚合初始化吗?

C++ - 成员变量聚合初始化

C++ 中聚合的带括号初始化的模板参数推导

可以使用 C++ 聚合初始化来构造实现接口的类的实例吗?

C++ 不能用初始值设定项列表初始化非聚合错误

创建线程安全单例 (C++) 时的临界区初始化