我正在阅读 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=
有两个操作数表达式:引用i
andi++ + 1
,两者都没有顺序。第二个对 有副作用i
,但在我看来第一个没有副作用或用于值计算(或者是引用“使用相同标量对象的值进行值计算”?它实际上取决于存储在 i 中的值?不这么认为),所以它不是未定义的行为;operator=
执行是在两个操作数评估之后排序的。它对 有副作用i
,但参考两个操作数的顺序很好,因此它不是未定义的行为;i++ + 1
显然是定义的行为。
我在这里错了吗?还是由于其他原因,这条线未定义的行为?
PS。标准实际上说
运算符的操作数的值计算在运算符结果的值计算之前排序。
,并且在这种情况下根本没有提到副作用。然而,排序关系仅在表达式评估之间定义,并且评估=值计算+副作用。所以要么我必须假设这个草案在这里不一致,或者假设在这一行中他们意味着评估而不是价值计算。还是我在这里错了?
编辑:
我想我会在这里回答自己,但这就是我困惑的原因:
5 1 表达式是指定计算的一系列运算符和操作数。表达式可能会产生一个值并可能导致副作用。
所以运算符的操作数本身不是子表达式。因此,仅对整体i = i++ + 1;
的值计算进行排序,标准中没有提及副作用排序。这就是它未定义的原因。
请注意,如果例如。operator=
对于给定的类型被重载(所以这将是一个隐式函数调用)它不会是未定义的行为,对吧?