在初始化程序列表中,同一个variables的多个variables是未定义的行为
考虑下面的代码:
int main() { int count = 0 ; int arrInt[2] = { count++, count++ } ; return 0 ; }
如果我们使用clang -std=c++03
编译代码,则会产生以下警告( 现场示例 ):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced] int arrInt[2] = { count++, count++ } ; ^ ~~
我不提倡这样的代码,但是类似的代码出现在另一个问题上,并且根据标准的C ++ 11是否定义了代码 。 在C ++ 11中,此行为是根据初始化程序列表中的多个突变未定义的行为定义的行为 ,实际上,如果使用-std=c++11
则警告消失。
如果我们看一个预先的C ++ 11 草案标准,它不具有相同的语言覆盖初始化列表,所以看起来我们留下了Chapter 5
expression式第4段,其中说:
除非另有说明,否则个体运算符的操作数和个别expression式的子expression式的评估顺序以及副作用发生的顺序是未指定的。 57)在前一个序列点和下一个序列点之间,一个标量对象应该通过评估一个expression式来最多修改其存储值一次。 此外,只有在确定要存储的值时才能访问先前值。 对于一个完整expression式的子expression式的每个可允许的sorting,应满足本段的要求; 否则行为是不确定的。
为了这个是未定义的 ,我们似乎必须解释count++, count++
作为一个expression式 ,因此每个count++
作为一个子expression式 ,那么这个代码是不是undefined 。
代码不是未定义的前C ++ 11,但评估顺序是未指定的 。 如果我们看一下标准草案1.9
节程序执行段落12说:
完整expression式是不是另一个expression式的子expression式的expression式。 […]
第15段说:
每个完整expression式的评估完成后有一个序列点12) 。
那么问题是count++, count++
是否是一个完整的expression式 ,每个count++
是一个子expression式,还是每个count++
它自己的完整expression式 ,因此在每个expression式之后都有序列点 ? 如果我们从8.5
节看初始化语法:
initializer-clause: assignment-expression { initializer-list ,opt } { } initializer-list: initializer-clause initializer-list , initializer-clause
我们唯一的expression式是一个赋值expression式 ,
分离组件是初始化程序列表的一部分,而不是expression式的一部分,因此每个count++
都是一个完整的expression式 ,每个expression式之后都有一个序列点 。
这个解释被下面的gcc
错误报告所证实,这个错误报告 和我的代码非常相似( 在我发现这个bug报告之前,我想到了我的例子 ):
int count = 23; int foo[] = { count++, count++, count++ };
最终成为缺陷报告430 ,我将引用:
[…]我相信标准很清楚,上面的每个初始化expression式都是一个完整expression式(1.9 [intro.execution] / 12-13;另见issue 392),因此每个expression式之后都有一个序列点(1.9 [intro.execution] / 16)。 我同意这个标准似乎没有规定expression式被评估的顺序,也许它应该。 有谁知道编译器不会评估expression式从左到右?