为什么我的结构的成员没有使用 `{}` 正确初始化?

2022-01-18 00:00:00 initialization c++ c++-faq

我有以下代码:

#include <iostream>结构 T{整数a,b,c;};主函数(){T t = {0};std::cout <<t.a <<',' <<t.b <<',' <<t.c <<'
';}

输出:

0,0,0

多年来,这段代码在关键的生产环境中愉快地运行,服务于一个重要的功能,项目的需求发生了变化,我需要输出为 1,1,1.p>

所以,我将 {0} 更改为 {1}:

#include <iostream>结构 T{整数a,b,c;};主函数(){T t = {1};std::cout <<t.a <<',' <<t.b <<',' <<t.c <<'
';}

输出:

1,0,0

我希望改为 1,1,1.

为什么我的 struct 的成员没有全部正确初始化?

解决方案

当你写 = {0} 时,那只会显式初始化第一个成员;其余部分根据标准隐式初始化为零,因此乍一看似乎您使用您编写的 0 显式初始化所有成员,但是您没有.

你写0的地方只影响第一个成员.因此,有一天,当您将其更改为 1 并认为它会更改所有成员时,您就会遇到一个错误,就像这里一样.这是误导/危险/愚蠢/脆弱的代码.

因此,如果没有随附的解释性注释,= {0} 将无法通过我团队的代码审查.你原本应该这样写:

T t = {};

现在,要根据新要求解决您的问题,您应该编写:

T t = {1,1,1};

或者,如果您不介意您的 struct 可能会失去 POD 特性,请给 T 一个构造函数.


正式的措辞

<块引用>

[C++11: 8.5.1/2]: 当聚合被初始化器列表初始化时,如 8.5.4 中所指定,初始化器列表的元素被视为初始化器对于聚合的成员,按递增的下标或成员顺序.每个成员都从相应的 initializer-clause 复制初始化.如果 initializer-clause 是一个表达式,并且需要一个窄化转换 (8.5.4) 来转换该表达式,则程序是非良构的.[..]

[C++11: 8.5.1/6]: 如果 initializer-clauses 的数量,则 initializer-list 是格式错误的em> 超出了要初始化的成员或元素的数量.

[C++11: 8.5.1/7]: 如果列表中的 initializer-clauses 少于聚合中的成员数,则每个未显式初始化的成员都应从一个空的初始化列表中初始化(8.5.4).

I had the following code:

#include <iostream>

struct T
{
   int a, b, c;
};

int main()
{
   T t = {0};
   std::cout << t.a << ',' << t.b << ',' << t.c << '
';
}

Output:

0,0,0

After many years of this code running happily in a critical production environment, serving a vital function, the requirements of the project changed and I needed the output to be 1,1,1.

So, I changed {0} to {1}:

#include <iostream>

struct T
{
   int a, b, c;
};

int main()
{
   T t = {1};
   std::cout << t.a << ',' << t.b << ',' << t.c << '
';
}

Output:

1,0,0

I expected 1,1,1 instead.

Why are my struct's members not all being initialised properly?

解决方案

When you write = {0}, that only explicitly initialises the first member; the rest are zero-initialised implicitly according to the standard, so it appears at first glance that you explicitly initialised all members with the 0 that you wrote, but you didn't.

That place where you wrote 0 only affects the first member. So when, one day, you changed it to 1 thinking that it'll change all members, you'll have a bug, like here. It's misleading/dangerous/silly/fragile code.

For that reason, without an accompanying explanatory comment, = {0} will not pass code review in my team. You should originally have written:

T t = {};

And now, to solve your problem according to the new requirements, you should write:

T t = {1,1,1};

or, if you don't mind your struct potentially losing POD-ness, give T a constructor.


Formal wording

[C++11: 8.5.1/2]: When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed. [..]

[C++11: 8.5.1/6]: An initializer-list is ill-formed if the number of initializer-clauses exceeds the number of members or elements to initialize.

[C++11: 8.5.1/7]: If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).

相关文章