我们都知道表达式
i++ + i++
是未定义的行为。 但如果副作用发生在函数体中的引用上,这也是 UB 吗? 例如:
int f(int& i) { // pass by reference
return i++;
}
int main() {
int v=5;
auto r=f(v)+ 2*f(v);
cout << r <<endl;
}
当我阅读标准时,我明白这就是UB:
该标准解释说,调用函数时修改值可以被视为副作用。 所以
f()
会对 v
产生副作用 :
C++23/[intro.execution]/7: (...)、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用,这是执行环境状态的变化。
该标准还解释了同一内存位置上未排序的副作用是 UB。 因此,我知道 + 的两个操作数的求值是无序的:
C++23/[intro.execution]/10:除非另有说明,否则各个运算符的操作数和各个表达式的子表达式的求值都是无序的。 (...)
如果内存位置上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中任何对象的值进行的值计算是无序的,并且它们不是潜在并发的(6.9.2),行为未定义。
因此,由于对
f()
的两次调用都修改了相同的内存位置v
,我知道它是UB。 然而,有些人认为,两个被调用函数体的执行将是不确定顺序的,而不是无顺序的,因此代码只会传递一个未指定的结果(如果首先执行 left 调用,则为 17,如果相反,则为 16),而不是 UB 。 有语言律师可以启发我吗?
[intro.execution]/11有效地表明每个函数调用在排序方面都是原子发生的,即多个函数调用不能交错。
不同函数调用(或一般不同的完整表达式)中的求值不能相对于另一个函数不排序(如果它们发生在同一线程上)。
因此这不是未定义的行为。两个
i++
评估的顺序不确定,因此您不知道哪个先发生,但它们并不是无序的。