考虑如下函数:
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 :(得分:7)
&&
(逻辑和),||
(逻辑或)和?
(三元运算符)。对于其余的运算符,这是(可选)优化。
表达式fact(5)
中0 * fact(5)
的求值通常不能仅仅因为您知道整个表达式的结果是0
而被优化,因为调用{{ 1}}可能会带来副作用(例如,修改某些全局变量),因此必须调用它。
如this comment中所述,如果一个好的编译器可以证明没有副作用,那么它将优化对fact()
的调用。
答案 1 :(得分:2)
它不是从左到右求值,是否看到0 *(无符号整数),并且知道右值将为0?
可以,如果标准要求的话。
但事实并非如此。
短路根本不是乘法的事情。他们本来可以做到的,但是可以说会令人困惑。
我们所有人都习惯f() || g()
跳过对g()
的呼叫,但是您真的希望0 * g()
做同样的事情吗?特别是因为0
只是十亿个可能整数中的一个?这将是一个奇怪的特定功能。 (相比之下,true
是仅两个布尔值之一。)
这与副作用无关,因为如果g()
返回f() || g()
,f()
将跳过true
的副作用。就是这样。
在实践中,如果编译器知道g()
没有副作用(因此程序的行为),可以取消对0 * g()
中g()
的调用不会被更改),但这并不是短路;那是"optimisation"。