为什么在C ++ 11中`i = i ++ + 1`未定义的行为?

时间:2012-05-28 02:01:09

标签: c++ c++11

我正在阅读n3290 C ++ 11标准草案(尽可能接近实际的标准文本),我注意到i = i++ + 1;产生了未定义的行为。我以前见过类似的问题,但是它们是根据较旧的标准(序列点)来回答的。新标准在表达式和子表达式执行之间引入排序之前/之后关系的概念。

  

1.9 13之前的序列是不对称的,传递的,成对关系   由单个线程(1.10)执行的评估之间的诱导   这些评估中的部分顺序。给出任何两个评估A.   和B,如果A在B之前排序,那么A的执行应该   在执行B之前。如果A在B和B之前没有排序   在A之前未排序,则A和B未被排序。 [注意:   执行未经测试的评估可能会重叠。 - 尾注]   当A为A时,评估A和B是不确定的   在B或B在A之前测序之前测序,但未指定   哪一个。 [注意:不确定顺序的评估不能重叠,   但要么可以先执行。 - 后注]

     

1.9 14每个价值   与完整表达相关的计算和副作用是   在每个值计算和副作用相关之前排序   使用下一个要评估的完整表达式。

     

1.9 15除此之外   注意到,对个体经营者和经营者的操作数进行评估   单个表达式的子表达式是无法排序的。 [注意:在   在执行期间多次计算的表达式   一个程序,未经测序和不确定的顺序评估   它的子表达式不需要在不同的情况下一致地执行   评估。 -end note]一个操作数的值计算   运算符在值计算结果之前排序   运营商。如果对标量对象的副作用未被排除   相对于同一标量物体的异常效应或a   使用相同标量对象的值进行值计算   行为未定义。

[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

我理解它的方式,就像那样:

  • operator=有两个操作数表达式:引用ii++ + 1,两者都不相同。第二个对i有副作用,但第一个似乎没有副作用或用于值计算(或参考“使用相同标量对象的值进行值计算”?它实际上取决于存储在i中的值吗?不这么认为),所以它不是未定义的行为;
  • operator=执行在两个操作数评估后排序。它对i有副作用,但它在引用两个操作数时排序很好,因此它不是未定义的行为;
  • i++ + 1显然是明确定义的行为。

我在这里有什么不对吗?或者由于某些其他原因,这一行是未定义的行为吗?

PS。标准实际上说

  

对运算符的操作数的值计算进行排序   在运算符结果的值计算之前。

,它根本没有提到副作用。然而,序列关系仅在表达式评估和评估=值计算+副作用之间定义。所以要么我必须假设这个草案在这里不一致,要么假设在这一行中他们意味着评价而不是价值计算。或者我错了吗?

编辑:

我想我会在这里回答自己,但这就是我混淆的原因:

  

5 1表达式是一系列运算符和操作数   指定计算。表达式可以产生值并且可以   引起副作用。

因此,运算符的操作数本身不是子表达式。因此,仅对整个i = i++ + 1;的值计算进行测序,并且没有提及标准的副作用测序。这就是它未定义的原因。

请注意,例如。 operator=因给定类型而被重载(因此它将是一个隐含的函数调用)它不会是未定义的行为,对吗?

3 个答案:

答案 0 :(得分:7)

这是“未定义的行为”,而不是“未指明”。未定义意味着允许机器执行任何操作,包括输出空程序,随机终止或爆炸。当然,当移植到另一个平台时,一个微妙的意外值更有可能产生结果。

未定义的行为适用于两个副作用适用于相同标量但未相对于彼此排序的任何情况。在这种情况下,副作用恰好相同(从表达式之前的原始值增加i),但是通过标准字母,它们组合起来产生UB。

副作用未被排除,因为除,?:||&&之外,运算符不会按照C ++ 11等术语定义排序规则§5.15/ 2:

  

如果计算第二个表达式,则在每个值计算和与第二个表达式相关的副作用之前,对与第一个表达式相关的每个值计算和副作用进行排序。

赋值运算符确定了一个特殊的排序规则,§5.17/ 1:

  

在所有情况下,赋值在右和左操作数的值计算之后,以及赋值表达式的值计算之前进行排序。

这对i = i ++ + 1无效,因为i ++的副作用不属于任何值计算。

答案 1 :(得分:4)

在C ++ 03中,i = ++i + 1;i = i++ + 1都没有明确定义。

但是在C ++ 11中,i = ++i + 1变得很明确。但i=i++ + 1仍然没有。

查看此内容以获取详细信息 Sequencing rules and example disagree

答案 2 :(得分:3)

您将值计算与副作用的分辨率混淆。虽然必须在赋值之前计算i++的值,但除了完整表达式的完成之外,没有任何内容对分配的副作用(对i的修改)进行排序。

对于一个对比的例子,看一下逗号运算符:"在与右表达式相关的每个值计算和副作用之前,对与左表达式相关的每个值计算和副作用进行排序。"请注意如何单独提及值计算和副作用。没有这样的转让规则。