19

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

1.9 13 之前排序是由单个线程 (1.10) 执行的评估之间的不对称、传递、成对关系,这会在这些评估中产生偏序。给定任意两个评估 A 和 B,如果 A 在 B 之前排序,则 A 的执行将在 B 的执行之前。如果 A 没有在 B 之前排序并且 B 没有在 A 之前排序,那么 A 和 B 是无序的。[注意:未排序评估的执行可以重叠。—尾注] 当 A 在 B 之前排序或 B 在 A 之前排序时,评估 A 和 B 的排序不确定,但未指定哪个。[注意:不确定顺序的评估不能重叠,但可以先执行。——尾注]

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

1.9 15 除非另有说明,对单个运算符的操作数和单个表达式的子表达式的求值是无序的。[注意:在程序执行期间多次评估的表达式中,其子表达式的未排序和不确定排序的评估不需要在不同的评估中一致地执行。—尾注] 运算符的操作数的值计算在运算符结果的值计算之前排序。如果标量对象上的副作用相对于同一标量对象上的另一个副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。

[ 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=有两个操作数表达式:引用iand i++ + 1,两者都没有顺序。第二个对 有副作用i,但在我看来第一个没有副作用或用于值计算(或者是引用“使用相同标量对象的值进行值计算”?它实际上取决于存储在 i 中的值?不这么认为),所以它不是未定义的行为;
  • operator=执行是在两个操作数评估之后排序的。它对 有副作用i,但参考两个操作数的顺序很好,因此它不是未定义的行为;
  • i++ + 1显然是定义的行为。

我在这里错了吗?还是由于其他原因,这条线未定义的行为?

PS。标准实际上说

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

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

编辑:

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

5 1 表达式是指定计算的一系列运算符和操作数。表达式可能会产生一个值并可能导致副作用。

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

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

4

3 回答 3

7

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

未定义的行为适用于两个副作用应用于同一标量而没有相对于彼此排序的任何情况。在这种情况下,副作用恰好是相同的(两者都i从表达式之前的原始值递增),但是按照标准的字母,它们结合起来产生了 UB。

副作用是未排序的,因为除了,?:||和之外&&,运算符没有按照 C++11 §5.15/2 等术语定义排序规则:

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

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

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

这无济于事i = i ++ + 1,因为 的副作用i ++不是任何值计算的一部分。

于 2012-05-28T05:30:56.603 回答
4

在 C++03 中,两者i = ++i + 1;i = i++ + 1 没有很好的定义。

但在 C++11 中,它i = ++i + 1变得定义良好。但i=i++ + 1仍然不是。

详细信息请查看此处的内容 排序规则和示例不一致

于 2012-05-28T02:12:00.353 回答
3

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

对于一个对比示例,看一下逗号运算符:“与左表达式相关的每个值计算和副作用都在与右表达式相关的每个值计算和副作用之前排序。” 请注意如何分别提到值计算和副作用。没有这样的分配规则。

于 2012-05-28T02:05:45.007 回答