34

考虑:

int f () {
    static int i = 0;
    return i++;
}

struct Test {
    int a, b;
    Test () : a(f()), b(f()) {}
};

Test t;

我知道由于它们a在.bstruct

我也知道对fin的两个调用g(f(), f())是无序的。

所以我想知道是否可以保证t.a == 0并且t.b == 1

4

2 回答 2

31

所以我想知道是否可以保证t.a == 0并且t.b == 1

只要在类声明a之前出现并且在and的初始化之间没有其他调用,这将始终是正确的。类成员按照它们在类中声明的顺序进行初始化。[class.base.init]/11:bf()ab

在非委托构造函数中,初始化按以下顺序进行:[...]

  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管 mem-initializers 的顺序)。

因此,既然a在此之前b,当构造函数初始化时a,它将f()第一次调用,然后在初始化时第二次调用它b

我们也知道成员初始化器之间有一个序列点,因为 [class.base.init]/7:

[...]每个 mem-initializer 执行的初始化构成一个完整的表达式。mem-initializer 中的任何表达式都被评估为执行初始化的完整表达式的一部分。

告诉我们每个初始化器都是一个完整的表达式,并且每个完整的表达式都是有序的:[intro.execution]/14

与完整表达式关联的每个值计算和副作用在与要评估的下一个完整表达式关联的每个值计算和副作用之前排序。

于 2017-04-18T19:01:42.597 回答
6

我知道由于它们在结构中声明的顺序,a 在 b 之前被初始化。

确实如此。

我对该约束的解释是,除非初始化表达式的评估在a初始化之前完成,否则无法在之前初始化。bb

我在标准中没有看到任何关于对用于初始化非静态成员的表达式的评估进行排序的内容。但是,我在 C++11 标准 (12.6.2/12) 中看到了以下示例:

mem-initializer 的 expression-list 或 braced-init-list 中的名称在为其指定 mem-initializer 的构造函数的范围内进行评估。[示例:

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) { }
};

除非在初始化this->i之后对 的评估进行排序,否则这将是无效的。i

于 2017-04-18T18:59:26.590 回答