为什么不将 C++ 结构初始化为 `= {0}` 会将其所有成员设置为 0?

Gab*_*les 4 c++ struct initialization zero-initialization

在做了大量测试并写下这个答案之后(注意:在我完全重写它之前对它的反对票),我不明白为什么= {0}不将结构的所有成员设置为零!

如果你这样做:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);
Run Code Online (Sandbox Code Playgroud)

...输出是:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150
Run Code Online (Sandbox Code Playgroud)

...虽然我预计输出是这样的:

d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0
Run Code Online (Sandbox Code Playgroud)

...这意味着只有 FIRST 成员被设置为零,其余的都被设置为默认值。

我一直认为以这 3 种方式中的任何一种初始化结构都会将其初始化为零,但显然我错了!

  1. data_t d{}
  2. data_t d = {}
  3. data_t d = {0}

因此,我从这个答案中得出的主要结论是:

这里最大的收获是这些:data_t d{}, data_t d = {}, and 中的data_t d = {0}任何一个实际上都将结构的所有成员设置为零!

  1. data_t d{} 将所有值设置为结构中定义的默认值。
  2. data_t d = {} 还将所有值设置为其默认值。
  3. 并且data_t d = {0}仅将 FIRST 值设置为零,并将所有其他值设置为其默认值。

那么,为什么不初始化 C++ 结构以= {0}将其所有成员设置为 0?

请注意,我上面的关键要点实际上与我多年来一直使用的看起来相当官方的文档相矛盾(https://en.cppreference.com/w/cpp/language/zero_initialization),它说T t = {} ;并且T {} ;都是零初始化器,实际上,根据我的测试和上面的总结,它们不是。

参考:

  1. 如何在 C++ 中将结构初始化为 0
  2. 更新:我也刚刚提到了这个引用:初始化对象时 {0} 是什么意思?

Nat*_*ica 8

那么,为什么不将 C++ 结构初始化为 = {0} 将其所有成员设置为 0?

因为您只提供一个值,而该类有多个成员。

当您拥有T t{};T t = {}正在执行的操作称为值初始化。在值初始化中,如果对象/成员没有默认构造函数或默认成员初始值设定项,则编译器将回退到零初始化对象/成员。所以与

data_t d{}
Run Code Online (Sandbox Code Playgroud)

成员以价值为100,-100,0,150以及0num3发生的,因为它没有缺省值,你没有在提供值{},因此编译回落到零初始化num3。这与data_t d = {}. 有了data_t d = {0}您提供的第一个元素,所以num10的,但然后像前两个,其他所有成员都与它们的默认值,如果他们有一个,或零,如果他们不这样做初始化初始化,给你0,-100,0 , 150 为成员值。

这是在 C++11 发布并允许默认成员初始值设定项时发生的更改。


如果你data_t被定义为

typedef struct
{
    int num1;
    int num2;
    int num3;
    int num4;
} data_t;
Run Code Online (Sandbox Code Playgroud)

那么data_t d{}, data_t d = {},data_t d = {0}都会给你一个零初始化的类,因为没有默认的成员初始值设定项,并且你在你的braced-init-list(技术名称{...})中提供的唯一值为零,所以所有成员都为零。


rus*_*tyx 5

data_t d3 = {0}列表初始化语法,它使用诸如 之类的聚合data_t执行聚合初始化:提供的0值用于初始化第一个成员,其余成员使用其相应的默认值进行初始化,如果不存在,则进行值初始化强调我的,为 C++14 编辑):

如果初始化器子句的数量少于成员数或初始化器列表完全为空,则其余成员由其默认成员初始化器初始化(如果在类定义中提供),否则由空列表初始化,按照通常的列表-initialization规则(其执行值初始化用于非类类型和非聚集体类使用默认的构造,和集合初始化为聚集体)。如果引用类型的成员是这些剩余成员之一,则程序格式错误。

值初始化意味着非类类型的零初始化。就是为什么num3没有默认值的成员获取值的原因0

注意:这不要与default-initialization混淆,后者根本不初始化非类类型。data_t d3;将是default-initialization,成员num3将处于不确定状态。

需要注意的重点是被初始化的对象是否是一个聚合,因为聚合和带有构造函数的类的初始化规则是不同的。在构造函数的情况下,没有默认值的非类成员将被默认初始化(即处于不确定状态)。

一些例子:

struct A { // an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
};

struct B { // not an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
    B() {}
    B(int) {}
};

int main() {
    A a1; // default-initialization: a1 is {100, -100, ???}
    A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
    A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
    A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
    B b1; // default-initialization: b1 is {100, -100, ???}
    B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
    B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
    B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}
Run Code Online (Sandbox Code Playgroud)

另请注意,聚合初始化规则早于 C++11。例如,请参阅此相关的 C++11 之前的问题:初始化对象时 {0} 是什么意思?