这是未定义的行为:
void feedMeValue(int x, int a) {
cout << x << " " << a << endl;
}
int main() {
int a = 2;
int &ra = a;
feedMeValue(ra = 3, a); // equivalent to: feedMeValue(a = 3, a) (see note bellow)
return 0;
}
因为我们可以先调用(3, 2)
或(3, 3)
,然后根据所评估的参数进行评估。
但是这个:
void feedMeReference(int x, int const &ref) {
cout << x << " " << ref << endl;
}
int main() {
int a = 2;
int &ra = a;
feedMeReference(ra = 3, a); // equivalent to: feedMeReference(a = 3, a) (see note bellow)
return 0;
}
将始终输出3 3
,因为第二个参数是引用,并且所有参数都已在函数调用之前进行了求值,因此即使在ra = 3
之后评估第二个参数,该函数也会收到在评估时,对a
的引用将具有值2
或3
,但在函数调用时将始终具有值3
。
第二个例子是UB吗?重要的是要知道,因为如果编译器检测到未定义的行为,它可以自由地执行任何操作,即使我知道它总会产生相同的结果。
注意:我将离开feedMeReference(ra = 3, a)
,因为某些答案会引用ra
,但您应该注意,如果我们调用feedMeReference(a = 3, a)
,则更简单的等效问题(更简单,因为我们消除ra
这就是我们的问题(第二个参数是参考)。)
答案 0 :(得分:12)
这是一个有趣的问题。在你的第一个案例中,有
未定义的行为,因为对象被修改了
在没有插入序列点的情况下访问(在语言中
C ++ 03 --- C ++ 11使用不同的语言来表达
一样)。在第二个,没有未定义的行为,
因为用左值初始化引用不能访问
对象,因此唯一的访问权限是ra = 3
。 (打电话给
函数建立一个序列点,所以访问
函数在它们和ra = 3
之间有一个序列点。)