初始化程序中的多个突变是否列出未定义的行为?

0x4*_*2D2 21 c++ undefined-behavior initializer-list c++11

我很好奇初始化列表和序列点.我刚才读到初始化程序列表中的评估顺序是从左到右.如果是这样,那么评估点之间肯定会有某种序列点,我错了吗?那么说是以下有效代码?是否有任何导致其未定义的行为?

int i = 0;

struct S {
    S(...) {} 
    operator int() { return i; }
};

int main() {
    i = S{++i, ++i};
}
Run Code Online (Sandbox Code Playgroud)

任何和所有回复都表示赞赏.

And*_*owl 18

是的,代码有效且没有未定义的行为.在评估构造函数之前,从左到右评估initizalizer列表中的表达式并对其进行排序S.因此,您的程序应始终2为变量赋值i.

引用C++标准的第8.5.4节:

"在braced-init-list的initializer-list中,初始化子句,包括包扩展(14.5.3)产生的任何结果,按照它们出现的顺序进行评估.也就是说,每个值计算和侧面与给定初始化子句相关联的效果每个值计算与初始化列表的逗号分隔列表中跟随它之后的任何初始化子句相关联的副作用之前进行排序.

因此,会发生什么:

  1. ++i得到评估,屈服i = 1(S构造函数的第一个参数);
  2. ++i得到评估,屈服i = 2(S构造函数的第二个参数);
  3. S的构造函数被执行;
  4. S执行转换运算符,返回值2;
  5. 2被赋值给i(已经有值2).

标准的另一个相关段落是§1.9/15,其中也提到了类似的例子有不确定的操作:

i = v[i++]; // the behavior is undefined
i = i++ + 1; // the behavior is undefined
Run Code Online (Sandbox Code Playgroud)

但是,同一段落说:

" 除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估都是无序的.[...]当调用函数时(无论函数是否为内联函数),每个值计算和与任何参数表达式相关的副作用或用后缀表达式指定调用的函数,在调用函数的身体的每一个表达式或语句执行前的测序. "

从1)初始化列表中的表达式的评估是从左到右排序的,2)S在评估初始化列表中的所有表达式之后对构造函数的执行进行排序,以及3)对i之后的赋值进行排序.S(及其转换运算符)的构造函数的执行,行为是明确定义的.