当将函数的返回值乘以零时,为什么它不短路?

时间:2019-12-27 17:57:27

标签: c++ return lazy-evaluation short-circuiting

考虑如下函数:

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

2 个答案:

答案 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"