成员初始化列表中的递增是否会生成未定义的行为?

Two*_*nds 24 c++ undefined-behavior language-lawyer ctor-initializer

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

class Wrinkle {
public:
    Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:
    int a;
    int x;
    int b;
};
Run Code Online (Sandbox Code Playgroud)

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

Hol*_*olt 31

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

[class.base.init]#7

[ 注意:每个mem-initializer执行的初始化构成一个完整表达式.mem-initializer中的任何表达式都将作为执行初始化的full-expression的一部分进行计算.]

[intro.execution]

5.一个全表达是

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

9.在与要评估的下一个全表达式相关联的每个值计算和副作用之前,对与全表达相关联的每个值计算和副作用进行排序.


但要注意:

[class.base.init]#13

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

  • [...]

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

所以,你的代码将有效地分配i + 1a,i + 2xi + 3b.


Tri*_*dle 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);
Run Code Online (Sandbox Code Playgroud)

接下来是一个注释:

[注意:每个mem-initializer执行的初始化构成一个完整表达式(4.6).mem-initializer中的任何表达式都将作为执行初始化的full-expression的一部分进行计算. - 结束说明]

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

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

短语"包括初始化程序的组成表达"强烈告诉我上述代码是合法的,因为++i在转到下一个初始化程序之前,已经完成了副作用.这只是我对标准的阅读,我很高兴推荐给比我更具有独立经验的人.

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

  • *"几乎与问题中的完全相同"* - 除了在这个例子中没有"a"的预增量,这是问题的要点. (8认同)