5

考虑以下代码:

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++ } ;
                           ^        ~~

我不提倡使用这样的代码,但类似的代码出现在另一个问题中,并且对于它是否根据标准 pre -C++11定义存在分歧。在C++11中,这种行为是明确定义的行为,根据Are multiple mutation within initializer 列出未定义的行为,实际上如果我使用,那么警告就会消失。-std=c++11

如果我们查看C++11之前的 草案标准,它没有相同的语言覆盖初始化器列表,所以我们似乎只剩下Chapter 5 表达式4段,它说:

除非另有说明,否则未指定单个运算符的操作数和单个表达式的子表达式的求值顺序,以及副作用发生的顺序。57)在前一个和下一个序列点之间,一个标量对象的存储值最多只能通过表达式的评估修改一次。此外,只能访问先验值以确定要存储的值。对于完整表达式的子表达式的每个允许排序,都应满足本段的要求;否则行为未定义。

为了使其未定义,似乎我们必须将其解释count++, count++表达式,因此每个都解释count++子表达式,那么这段代码在C++11之前是否未定义

4

1 回答 1

4

代码不是C++11之前未定义的,但评估顺序是未指定的。如果我们看一下标准部分草案程序执行12段说:1.9

完整表达式是不是另一个表达式的子表达式的表达式。[...]

15段说:

每个完整表达式12)的评估完成时都有一个序列点。

那么问题是是否count++, count++是一个完整的表达式和每个count++表达式,或者每个都是count++它自己的完整表达式,因此每个之后都有序列点8.5 如果我们从Initializers部分查看此初始化的语法:

initializer-clause:
  assignment-expression
  { initializer-list ,opt }
  { }
initializer-list:
  initializer-clause
  initializer-list , initializer-clause

我们唯一的表达式赋值表达式,分隔组件是初始化列表的一部分,而不是表达式的一部分,因此每个表达式count++都是完整的表达式,每个后面都有一个序列点

gcc 以下错误报告证实了这种解释,它的代码与我的非常相似(我在找到此错误报告之前想出了我的示例方式):

int count = 23;
int foo[] = { count++, count++, count++ };

最终作为缺陷报告 430,我将引用:

[...]我相信标准很清楚,上面的每个初始化表达式都是完整表达式(1.9 [intro.execution]/12-13;另见 issue 392),因此每个表达式之后都有一个序列点(1.9 [intro.execution]/16)。我同意该标准似乎并没有规定评估表达式的顺序,也许它应该。有谁知道编译器不会从左到右评估表达式?

于 2013-11-09T18:59:54.483 回答