逻辑与、或:左到右的评估是否有保证?

34
左到右的逻辑运算符(&&和||)的评估是有保证的吗?
假设我有这样一个例子:
SDL_Event event;

if (SDL_PollEvent(&event)) {
    if (event.type == SDL_QUIT) {
            // do stuff
    }
}

这个能保证和这个一样吗?
SDL_Event event;

if (SDL_PollEvent(&event) && event.type == SDL_QUIT) {
    // do stuff
}

这也可能非常重要,假设我们有两个要求,ab。要求ab更有可能失败。那么,使用if (a && b)if (b && a)更高效。

4
&& 表示的顺序非常重要,一个经典而重要的用途是在使用指针之前测试是否为空指针: if (a != 0 && a->b != 0 && a->b->c != 0) { somefunc(a->b->c->d); }。这个顺序对于避免核心转储非常关键。(为了说明问题,我忽略了洛德法则!) - Jonathan Leffler
1
@Jonathan:我忽略它是为了完成任何工作的目的! - Steve Jessop
1
这些不是“比较”运算符。 - Ben Voigt
1个回答

51

是的,这是有保证的,否则此类运算符将失去大部分的用处。

重要通知:这仅适用于内置的 &&||;如果某些人故意重载它们,则它们被视为“常规”的重载二元运算符,因此在这种情况下,两个操作数都会 总是 被计算,并以通常的未指定顺序进行。因此,请勿对其进行重载-它会破坏程序控制流的一个极其重要的假设。


相关标准引用

内置的 &&|| 具有保证的短路行为

§5.14 ¶1

& 不同,&& 保证从左到右计算:如果第一个操作数为 false,则不计算第二个操作数。

§5.15 ¶1

| 不同,|| 保证从左到右计算;此外,如果第一个操作数评估为 true,则不计算第二个操作数。

如果重载,则它们会像“常规”二元运算符一样行为(没有短路或保证的计算顺序)

§13.5 ¶9

未在子条款 13.5.3 到 13.5.7 中明确提到的运算符作为遵循 13.5.1 或 13.5.2 规则的普通一元和二元运算符。

&&|| 没有在这些子条款中明确提到,因此常规的 §13.5.2 适用:

§13.5.2 ¶1

一个二元操作符应该通过一个带有一个参数的非静态成员函数(9.3)或者带有两个参数的非成员函数来实现。因此,对于任何二元操作符 @x@y 可以被解释为 x.operator@(y) 或者 operator@(x,y)

没有特别的规定只计算一侧或按照特定的顺序计算。

(以上引用自C++11标准)


1
顺便提一下,记住,如果您有“简单”的条件(例如第二个条件),分支可能比评估条件本身更耗费资源。 - Matteo Italia
Matteo Italia:ab是假设性的。它们可能是IsPrime(n)IsEven(n),在这种情况下,正确的顺序非常明显,可以实现严格的节约。 - orlp
@nightcracker:当然,我只是指出一个小但常见的误解。 :) - Matteo Italia
1
没有副作用的条件可以被编译器移动到任何位置。编译器可以消除分支。 - Potatoswatter
1
@MatteoItalia:非常重要的是要注意,如果你重载 operator&&operator|| ,那么它就会被视为普通函数调用,并且左到右的计算顺序也不再保证。 - Mooing Duck
@MooingDuck:是的,确实非常重要;已经添加了备注和相关标准引用。 - Matteo Italia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接