考虑:
int f () {
static int i = 0;
return i++;
}
struct Test {
int a, b;
Test () : a(f()), b(f()) {}
};
Test t;
我知道a
是在b
之前初始化的,因为它们在struct
中的声明顺序。
我也知道,g(f(), f())
中对f
的两次调用是没有顺序的。
所以我想知道是否可以保证t.a == 0
和t.b == 1
?
想知道是否可以保证
t.a == 0
和t.b == 1
?
只要a
在类声明中b
之前,并且在a
和b
的初始化之间没有其他调用f()
,这将始终是正确的。 类成员按照它们在类中声明的顺序进行初始化。 [class.base.init]/11:
在非委托构造函数中,初始化按以下顺序进行:[...]
- 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,无论 mem 初始值设定项的顺序如何)。
因此,由于a
先于b
,因此当构造函数初始化a
时,它将第一次调用f()
,然后在初始化b
时第二次调用它。
我们还知道成员初始值设定项之间有一个序列点,因为 [class.base.init]/7:
[...]每个 mem 初始值设定项执行的初始化构成一个完整表达式。mem 初始值设定项中的任何表达式都作为执行初始化的完整表达式的一部分进行计算。
告诉我们每个初始值设定项都是一个完整表达式,并且每个完整表达式都是按顺序排列的:[intro.execution]/14
与全表达式相关的每个值计算和副作用在与要评估的下一个全表达式相关的每个值计算和副作用之前进行排序。
我知道 a 在 b 之前初始化,因为它们在结构中的声明顺序。
这是真的。
我对这种约束的解释是,除非在初始化之前完成初始值设定项表达式的计算b
否则a
不能在b
之前
初始化。我在标准中没有看到任何关于对用于初始化非静态成员的表达式的计算进行排序的内容。但是,我在 C++11 标准 (12.6.2/12) 中看到以下示例:
内存初始值设定项的表达式列表或大括号初始化列表中的名称在为其指定了 mem 初始值设定项的构造函数的范围内计算。[示例:
class X { int a; int b; int i; int j; public: const int& r; X(int i): r(a), b(i), i(i), j(this->i) { } };
除非在初始化i
后对this->i
的评估进行排序,否则这将无效。