嵌套类的值初始化
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:
- 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.
根据值初始化规则。值初始化发生:
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:
- 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.