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

j_k*_*bik 19 c++ c++11

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

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

1.9 14在与要评估的下一个全表达式相关的每个值计算和副作用之前,对与全表达式相关的每个值计算和副作用进行排序.

1.9 15除非另有说明,否则对个体操作员的操作数和个别表达式的子表达式的评估是不确定的.[注意:在程序执行期间不止一次评估的表达式中,不需要在不同的评估中一致地执行对其子表达式的未序列和不确定顺序的评估.-end note]运算符操作数的值计算在运算符结果的值计算之前排序.如果对标量对象的副作用相对于对同一标量对象的其他影响或使用相同标量对象的值进行的值计算未进行排序,则行为未定义.

[ 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 ]
Run Code Online (Sandbox Code Playgroud)

我理解它的方式,它的工作方式如下:

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

我在这里错了吗?或者由于某些其他原因,这行未定义的行为?

PS.标准实际上说

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

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

编辑:

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

5 1表达式是指定计算的运算符和操作数序列.表达式可能会导致值,并可能导致副作用.

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

请注意,例如.operator=为给定的类型重载(所以它将是一个隐含的函数调用)它不会是未定义的行为,对吧?

Pot*_*ter 7

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

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

所述副作用是未测序,因为除了,,?:,||,和&&,运营商不使用诸如C++ 11§5.15/ 2限定顺序规则:

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

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

在所有情况下,分配的右侧和左侧的操作数的值计算后测序,并赋值表达式的值计算之前.

这没有用,i = i ++ + 1因为副作用i ++不是任何值计算的一部分.