初始化列表中同一变量的多个突变在C ++ 11之前未定义行为 [英] Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11

查看:334
本文介绍了初始化列表中同一变量的多个突变在C ++ 11之前未定义行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码:

  int main()
{
int count = 0;
int arrInt [2] = {count ++,count ++};

return 0;
}



如果我们使用 clang -std = c ++ 03 会产生以下警告( live example ):

 警告:多次无序修改'count'[-Wunsequenced] 
int arrInt [2] = {count ++,count ++};
^ ~~



我不提倡像这样的代码,但类似的代码另一个问题,并且对是否根据标准的pre C ++ 11 定义存在分歧。在 C ++ 11 中,此行为是根据初始化程序列表中的多个突变未定义的行为明确定义的行为如果我使用 -std = c ++ 11 ,那么警告消失。



查看预先的 C ++ 11 草稿标准,它没有覆盖 initializer-list 的相同语言,所以看起来我们还剩下第5章 段落 ,表示:


57)在上一个和下一个序列点之间,标量对象的存储值必须被修改(例如,对于每个操作符的操作数和单个表达式的子表达式的求值,以及发生副作用的顺序)最多一次通过表达式的评价。此外,仅访问先前值以确定要存储的值。对于一个完整表达式的子表达式的每个允许排序,应满足本段的要求;


为了使它成为未定义 count ++,count ++ 解释为一个表达式,因此每个 count ++

解决方案 / div>

代码不是未定义 C ++ 11 ,但评估顺序未指定。如果我们看一下 1.9 段 1.9 段说明:


全表达式是一个表达式,不是另一个表达式的子表达式。 [...]


和段落 15 说:


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


那么问题是 count ++,count ++ 是一个完整表达式,每个计数++ a 子表达式或每个 count ++ 都是自己的是每个之后的序列点吗?如果我们从 8.5 初始化程序部分查看初始化的语法:

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

>我们有一个 assignment-expression 分隔组件是 initializer-list 的一部分,不是表达式的一部分,因此每个 count ++ 是一个完整表达式



这个解释由以下 gcc 错误报告,它与我的代码非常相似(我提出了我的例子方式之前我发现这个错误报告):

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

结尾为缺陷报告430 ,我将引用:


[...]我相信标准是清楚的,上面的每个初始化表达式是一个完全表达式(1.9 [intro.execution] / 12-13;也参见问题392),因此有一个每个表达式后的序列点(1.9 [intro.execution] / 16)。我同意这个标准似乎不支持表达式的计算顺序,也许它应该。有没有人知道一个编译器,不会评估从左到右的表达式?



Consider the following code:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

If we compile the code using clang -std=c++03 it produces the following warning(live example):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
    int arrInt[2] = { count++, count++ } ;
                           ^        ~~

I am not advocating for code like this but similar code came up in another question and there was disagreement over whether it is defined or not according to the standard pre-C++11. In C++11 this behavior is well defined behavior according to Are multiple mutations within initializer lists undefined behavior and indeed if I use -std=c++11 then the warning goes away.

If we look at a pre-C++11 draft standard it does not have the same language covering initializer-list so it seems we are left with Chapter 5 Expressions paragraph 4 which says:

Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.57) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.

In order for this to be undefined it would seem we would have to interpret count++, count++ as an expression and therefore each count++ as a subexpression, so is this code undefined pre-C++11?

解决方案

The code is not undefined pre-C++11 but the evaluation order is unspecified. If we look at the draft standard section 1.9 Program execution paragraph 12 says:

A full-expression is an expression that is not a subexpression of another expression. [...]

and paragraph 15 says:

There is a sequence point at the completion of evaluation of each full-expression12).

then the question is whether count++, count++ is a full expression and each count++ a sub-expression or is each count++ it's own full expression and therefore there is sequence point after each one? if we look at the grammar for this initialization from section 8.5 Initializers:

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

the only expression we have is an assignment-expression and the , separating the components is part of the initializer-list and and not part of an expression and therefore each count++ is a full expression and there is a sequence point after each one.

This interpretation is confirmed by the following gcc bug report, which has very similar code to mine(I came up with my example way before I found this bug report):

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

which ends up as defect report 430, which I will quote:

[...]I believe the standard is clear that each initializer expression in the above is a full-expression (1.9 [intro.execution]/12-13; see also issue 392) and therefore there is a sequence point after each expression (1.9 [intro.execution]/16). I agree that the standard does not seem to dictate the order in which the expressions are evaluated, and perhaps it should. Does anyone know of a compiler that would not evaluate the expressions left to right?

这篇关于初始化列表中同一变量的多个突变在C ++ 11之前未定义行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆