嵌套类的值初始化

Value initialization of nested classes

根据值初始化规则。值初始化发生:

1,5) when a nameless temporary object is created with the initializer consisting of an empty pair of parentheses or braces (since C++11);

2,6) when an object with dynamic storage duration is created by a new-expression with the initializer consisting of an empty pair of parentheses or braces (since C++11);

3,7) when a non-static data member or a base class is initialized using a member initializer with an empty pair of parentheses or braces (since C++11);

4) when a named variable (automatic, static, or thread-local) is declared with the initializer consisting of a pair of braces.

简单的例子

struct A{
    int i;
    string s;
    A(){};
};

A a{} 
cout << a.i << endl // default initialized value

没有显式声明的构造函数并保留了默认的默认构造函数 // 编译器生成了一个我们得到的构造函数。

struct A{
    int i;
    string s;

};
A a{};
cout << a.i << endl // zero-initialized value

但是使用另一个结构。

struct A{
    int i;
    string s;

};

struct B{
    A a;
    int c;
};

B a{};
cout << a.a.i << endl // default initialized , even tho we did not , int struct B , declared A a{}.

即使不使用 {} / () 构造,a.i 的值也是零初始化的,这违反了规则(如果我没记错的话)。

在结构 B 上使用相同的逻辑:

struct A{
    int i;
    string s;

};

struct B{
    A a;
    int c;
};

B b;
cout << b.c << endl; // default inicialized

我们根据规则获得行为。

最后一个例子:

struct A
{
    int i;
    A() { } 
};

struct B { A a; }; 

std::cout << B().a.i << endl;

B().a.i 也是零初始化,而我们显式声明了构造函数并且它没有被删除。

为什么这些值初始化为零?根据规定的规则 here 它们应该被默认初始化而不是零初始化。

感谢您的回答。

A是聚合还是非聚合的区别

[dcl.init.aggr](强调我的)

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11),

所以当 A 没有声明的构造函数时,说 A a{} 具有 aggregate initialization

的效果

这将用一个空的初始化列表构造每个成员:

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

所以你得到 int{}std::string{},它们会将整数成员值初始化为零。

当您提供默认构造函数时,聚合 属性 会丢失并且 int 成员保持未初始化状态,因此访问它被视为未定义行为。


具体来说:

这段代码在访问a.i时是未定义的行为,因为你提供了一个用户定义的构造函数,所以int i字段在构造后保持未初始化状态:

struct A{
    int i;
    string s;
    A(){};
};

A a{} ;
cout << a.i << endl;

并且此代码在访问 b.c 时表现出未定义的行为,因为您没有对 B b 执行列表初始化:

struct B{
    A a;
    int c;
};

B b;
cout << b.c << endl;

所有其他代码都没有问题,并将对整数字段进行零初始化。在您使用大括号 {} 的场景中,您正在执行 聚合初始化.

最后一个示例有点棘手,因为您正在执行 值初始化。由于B是一个聚合,它得到零初始化([dcl.init]),其中:

each base-class subobject is zero-initialized

因此,您再次可以访问 A 子对象的整数成员。

根据聚合初始化的规则,成员确实是值初始化的。

Value initialization:

In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.

The effects of aggregate initialization are:

  1. If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.