24

这会导致未定义的行为吗?具体来说,初始化列表中的递增以及如何对其进行评估。

class Wrinkle {
public:
    Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:
    int a;
    int x;
    int b;
};

成员声明和初始化列表之间的顺序差异是有意的,因为这是一个可以准确展示这种差异的示例,因此请暂时忽略它。

4

2 回答 2

30

这不会产生未定义的行为,因为:

[class.base.init]#7

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

[介绍.执行]

5.一个完整的表达是

  • [...]
  • 一个 init-declarator 或一个 mem-initializer,包括初始化器的组成表达式,

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


但请注意:

[class.base.init]#13

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

  • [...]

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

因此,您的代码将有效地分配i + 1ai + 2和。xi + 3b

于 2018-01-19T13:10:58.167 回答
6

C++17 标准包含一个与问题几乎完全相同的示例:

struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };
struct D : B1, B2 {
  D(int);
  B1 b;
  const int c;
};

D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ }
D d(10);

紧随其后的是一个注释:

[ 注意:每个 mem-initializer 执行的初始化构成一个完整的表达式(4.6)。mem-initializer 中的任何表达式都被评估为执行初始化的完整表达式的一部分。——尾注]

在链接之后,第 4.6 节告诉我们“完整表达式”的定义之一是

... 一个 mem 初始化器,包括初始化器的组成表达式,

“包括初始化器的组成表达式”这句话强烈地向我表明,上面的代码是合法的,因为 will 的副作用++i在继续下一个初始化器之前已经完成。这只是我对标准的阅读,我很乐意听从比我更有标准经验的人。

(同样值得注意的是,成员的初始化将按照它们在类中声明的顺序发生,而不是按照它们出现在成员初始化列表中的顺序发生)。

于 2018-01-19T13:00:30.610 回答