一个类的完整类上下文是一个
(6.1)功能主体,
(6.2) 默认参数,
(6.3)noexcept-specifier([except.spec]),
(6.4) 合同条件,或
(6.5)默认成员初始值设定项类的成员规范。 [注意:完整的类上下文 嵌套类也是任何封闭类的完整类上下文 类,如果嵌套类是在成员规范内定义的 封闭类。 —尾注]
上面突出显示的文本似乎为以下代码段提供了支持:
#include<iostream>
struct A{
int i = j + 1;
int j = 1;
};
int main(){
A a;
std::cout << a.i << '\n';
std::cout << a.j << '\n';
}
,我希望它能打印
2
1
GCC和clang打印
1
1
但另外clang会发出以下警告:
prog.cc:3:13: warning: field 'j' is uninitialized when used here [-Wuninitialized]
int i = j + 1;
^
prog.cc:8:7: note: in implicit default constructor for 'A' first required here
A a;
^
prog.cc:2:8: note: during field initialization in the implicit default constructor
struct A{
^
1 warning generated.
我的假设是代码是格式错误的NDR。但是为什么呢?
答案 0 :(得分:7)
在非委托的构造函数中,如果给定的可能构造的子对象未由mem-initializer-id指定(包括由于构造函数没有ctor-initializer而没有mem-initializer-list的情况),
如果该实体是具有默认成员初始化程序([class.mem])和两者之一的非静态数据成员,则
[...]构造函数的类不是联合体[...]
根据[dcl.init]中指定的默认成员初始化程序初始化实体;
所以,这意味着
struct A{
int i = j + 1;
int j = 1;
};
被翻译为
struct A{
A() : i(j + 1), j(1) {}
int i;
int j;
};
并且由于i
首先被初始化,因此它使用了未初始化的变量,并且是未定义的行为。
答案 1 :(得分:3)
我认为代码等于以下内容:
struct A{
int i;
int j;
A():i(j + 1),j(1){}
};
这说明编译器是正确的。因为成员是按照声明它们的顺序(在标准*中指定的位置)初始化的。就地 clarification 初始化应该只是在所有ctor中对其进行初始化的语法糖。因此,代码确实具有未定义的行为,因为j
是未初始化的变量。
编辑:*找到它[10.9.2初始化基础和成员](http://eel.is/c++draft/class.base.init)
在非委托构造函数中,初始化按以下顺序进行:
(13.1) 首先,并且仅对于大多数派生类([intro.object])的构造函数而言,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历时出现的顺序进行初始化,其中“从左到右”是基类在派生类base-specifier-list中的出现顺序。
(13.2) 然后,直接基类按照它们出现在base-specifier-list中的声明顺序进行初始化(与mem-initializer的顺序无关)。
(13.3) 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,无论mem-initializer的顺序如何)。
(13.4) 最后,执行构造函数主体的复合语句。
答案 2 :(得分:0)
这里的问题是您在声明和初始化j之前使用int i = j + 1
。
您应该做的是
struct A{
int j = 1;
int i = j + 1;
};
因此j应该在i之前初始化。希望这会有所帮助:)
答案 3 :(得分:-2)
在该示例中,您给变量j未初始化(GCC可能会将这些未初始化的积分变量默认为0),这就是为什么i的初始值为1,因此打印值为 顺便说一句,在大多数情况下,初始化变量不是一个好主意。而是使用构造函数,请参见this