考虑如下函数:
unsigned int fact(unsigned int i) {
if (i <= 1) { return 1; }
return i * fact(i-1);
}
如果我要实例化一个新的变量unsigned int f
这样f = 0 * fact(5)
,为什么它不"短路"?
unsigned int fact(unsigned int i) {
std::cout << "a";
if (i <= 1) { return 1; }
return i * fact(i-1);
}
int main() {
unsigned int f = 0 * fact(5);
}
此处的输出为aaaaa
.如果f
只能为零,那么假设它知道返回类型,它为什么要调用该函数?它不是从左到右评估,看到0 * (unsigned int)
并知道右值将被0
吗?
短路评估对于&&
(逻辑和(、||
(逻辑或(和?
(三元运算符(是强制性的。对于其余运算符,这是一个(可选(优化。
表达式0 * fact(5)
中fact(5)
的计算通常不能仅仅因为您知道整个表达式的结果是0
而优化,因为对fact()
的调用可能会引入副作用(例如,修改某些全局变量(,因此必须调用它。
正如这篇评论中所说,一个好的编译器如果能证明没有副作用,就会优化对fact(5)
的调用。
它是否不从左到右计算,请参阅 0 *(无符号整数(并知道右值将为 0?
它可以,如果标准告诉它,它会的。
但事实并非如此。
短路根本不是乘法的事情。他们本可以把它变成一件事,但可以说是令人困惑的。
我们都习惯于f() || g()
可能会跳过对g()
的调用,但你真的希望0 * g()
做同样的事情吗?特别是因为0
只是数十亿个可能的整数中的一个?这将是一个奇怪的特定功能。(相比之下,true
是仅有的两个布尔值中的一个。
这与副作用无关,因为如果f()
返回true
,f() || g()
会跳过g()
的副作用。事情就是这样。
在实践中,如果编译器知道g()
没有副作用(因此程序的行为不会改变(,它可以避免0 * g()
g()
调用,但这不是短路;这就是"优化"。