初始化程序列表中有多个突变是未定义的行为?
我很好奇初始化列表和序列点。 我刚才读到,初始化列表中的评估顺序是左alignment的。 如果那样的话,评价点之间肯定有某种顺序点,我错了吗? 所以说这是以下有效的代码? 有没有什么会导致未定义的行为?
int i = 0; struct S { S(...) {} operator int() { return i; } }; int main() { i = S{++i, ++i}; }
任何和所有的回应表示赞赏。
是的,代码是有效的,没有未定义的行为。 在对S
的构造函数进行评估之前,initizalizer列表中的expression式将从左到右进行求值并进行sorting。 因此,您的程序应该始终将值2
分配给variablesi
。
引用C ++标准的第8.5.4节:
“在braced-init-list的初始化程序列表中,初始化子句,包括从pack扩展(14.5.3)得到的任何初始化子句, 按它们出现的顺序进行评估。也就是说,每个值计算和side与给定初始化子句相关联的效果 在每个值计算和副作用 之前进行sorting, 并且与在初始化程序列表的逗号分隔列表中跟随它的任何初始化子句相关联。
因此,会发生什么事情是:
-
++i
得到评估,产生i = 1
(S
的构造函数的第一个参数); -
++i
得到评估,产生i = 2
(S
的构造函数的第二个参数); -
S
的构造函数被执行; -
S
的转换运算符被执行,返回值2
; - 值
2
被分配给i
(已经具有值2
)。
该标准的另一个相关段落是§1.9/ 15,其中也提到了类似的例子, 它们有不确定的行为:
i = v[i++]; // the behavior is undefined i = i++ + 1; // the behavior is undefined
但是,同一段说:
除了注意到的地方之外,对单个运算符和各个expression式的子expression式的操作数的评估是不确定的。[…]当调用一个函数 ( 函数是否是内联函数)时,每个与任何参数expression式相关的值计算和副作用,或者用指定被调用函数的后缀expression式,在执行被调用函数体内的每个expression式或语句之前进行sorting “。
由于1)初始值设定项列表中的expression式的评估是从左到右sorting的,2)在初始化项列表中所有expression式的求值之后, S
的构造函数的执行被sorting,以及3)对i
的赋值是在执行S
(及其转换运算符)的构造函数之后进行sorting,行为是明确的。
是的,你确实遇到了不确定的行为。
以下是导致未定义行为的情况示例:
- 一个variables在一个序列点内多次改变 。 作为一个典型的例子,i = i ++expression式经常被引用,其中ivariables的赋值和它的增量同时被执行。 要详细了解这类错误,请阅读“顺序点”部分 。
- 在初始化之前使用variables。 尝试使用该variables时发生未定义的行为。
- 内存分配使用新的[]运算符和后续版本使用删除运算符。 例如:T * p = new T [10]; 删除p ;. 正确的代码是:T * p = new T [10]; 删除[] p ;.
EDITED
还有你的代码S {++ i,++ i}; 没有编译为VS2012。 可能是你的意思是S(++ i,++ i);? 如果您使用“()”,则存在未定义的行为。 在其他情况下,你的源代码是不正确的。