如果声明 - 短路评估与可读性

rel*_*xxx 90 c++ if-statement side-effects short-circuiting

有时,if语句可能相当复杂或冗长,因此为了便于阅读,最好在之前提取复杂的调用if.

例如:

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

进入这个

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

(提供示例并不是那么糟糕,它仅用于说明...想象其他具有多个参数的调用等)

但是通过这种提取,我失去了短路评估(SCE).

  1. 我每次真的失去了SCE吗?是否存在允许编译器"优化"并仍提供SCE的情况?
  2. 有没有办法保持第二个片段的可读性提高而不会失去SCE?

Hor*_*man 118

一个自然的解决方案是这样的:

bool b1 = SomeCondition();
bool b2 = b1 || SomeOtherCondition();
bool b3 = b2 || SomeThirdCondition();
// any other condition
bool bn = bn_1 || SomeFinalCondition();

if (bn)
{
  // do stuff
}
Run Code Online (Sandbox Code Playgroud)

这具有易于理解,适用于所有情况和具有短路行为的益处.


这是我最初的解决方案:方法调用和for循环体的一个好模式如下:

if (!SomeComplicatedFunctionCall())
   return; // or continue

if (!SomeOtherComplicatedFunctionCall())
   return; // or continue

// do stuff
Run Code Online (Sandbox Code Playgroud)

一个人获得了短路评估相同的良好性能优势,但代码看起来更具可读性.

  • @JoulinRouge:很有意思,我从未听说过这个原则.我本人更喜欢这种"短路"方法,以获得可读性的好处:它减少了压痕并消除了在缩进块之后发生某些事情的可能性. (13认同)
  • @relaxxx:我知道了,但是"在`if`之后要做更多的事情"也表明你的功能或方法太大了,应该分成更小的.它并不总是最好的方式,但经常是! (4认同)
  • 这违反了白名单原则 (2认同)
  • 它更具可读性吗?正确地命名`b2`你会得到`someConditionAndSomeotherConditionIsTrue`,而不是超级有意义的.此外,在本练习中我必须在我的心理堆栈上保留一堆变量(并且直到我停止在此范围内工作).我会选择`SJuan76`的2号解决方案,或者只是把整个东西放在一个函数中. (2认同)
  • 我没有阅读所有的评论,但经过快速搜索后,我没有发现第一个代码片段的大优势,即调试.将内容直接放入if语句而不是事先将其分配给变量然后使用变量使调试变得比它需要的更困难.使用变量还允许在语义上将值组合在一起,从而提高可读性. (2认同)

Ami*_*ack 31

我倾向于将条件分解为多行,即:

if( SomeComplicatedFunctionCall()
 || OtherComplicatedFunctionCall()
  ) {
Run Code Online (Sandbox Code Playgroud)

即使在处理多个操作符(&&)时,您也只需要使用每对括号进行缩进.SCE仍在继续 - 无需使用变量.以这种方式编写代码使我多年来对它更加可读.更复杂的例子:

if( one()
 ||( two()> 1337
  &&( three()== 'foo'
   || four()
    )
   )
 || five()!= 3.1415
  ) {
Run Code Online (Sandbox Code Playgroud)


Som*_*ude 28

如果你有很长的条件链以及如何保持一些短路,那么你可以使用临时变量来组合多个条件.举个例子,就可以做例如

bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
if (b && some_other_expression) { ... }
Run Code Online (Sandbox Code Playgroud)

如果你有一个支持C++ 11的编译器,你可以使用lambda表达式将表达式组合成函数,类似于上面的:

auto e = []()
{
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
};

if (e() && some_other_expression) { ... }
Run Code Online (Sandbox Code Playgroud)


SJu*_*n76 21

1)是的,你不再拥有SCE.否则,你会有

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
Run Code Online (Sandbox Code Playgroud)

以某种方式工作,取决于以后是否有if声明.方式太复杂了.

2)这是基于意见的,但对于相当复杂的表达式,您可以这样做:

if (SomeComplicatedFunctionCall()
    || OtherComplicatedFunctionCall()) {
Run Code Online (Sandbox Code Playgroud)

如果它过于复杂,那么显而易见的解决方案是创建一个评估表达式并调用它的函数.


KII*_*IIV 21

您还可以使用:

bool b = someComplicatedStuff();
b = b || otherComplicatedStuff(); // it has to be: b = b || ...;  b |= ...; is bitwise OR and SCE is not working then 
Run Code Online (Sandbox Code Playgroud)

和SCE将工作.

但它的可读性并不比例如:

if (
    someComplicatedStuff()
    ||
    otherComplicatedStuff()
   )
Run Code Online (Sandbox Code Playgroud)

  • 我并不热衷于将布尔运算与一个按位运算符结合起来.这对我来说似乎不太合适.通常我会使用看起来最具可读性的东西,除非我的工作水平非常低且处理器周期都很重要. (3认同)
  • 我特意使用了'b = b || otherComplicatedStuff();`和@SargeBorsch进行编辑以删除SCE.感谢您注意到@Ant的变化. (2认同)

son*_*yao 14

1)我每次真的失去了SCE吗?编译器是否允许某些场景"优化它"并仍然提供SCE?

我不认为这种优化是允许的; 特别是OtherComplicatedFunctionCall()可能有一些副作用.

2)在这种情况下,最佳做法是什么?是否只有这种可能性(当我想要SCE时)才能直接拥有我需要的所有内容,并且"只是将其格式化为尽可能可读"?

我更喜欢将它重构为一个函数或一个具有描述性名称的变量; 这将保留短路评估和可读性:

bool getSomeResult() {
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
}

...

if (getSomeResult())
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

当我们getSomeResult()基于SomeComplicatedFunctionCall()和实现时OtherComplicatedFunctionCall(),如果它们仍然复杂,我们可以递归地分解它们.

  • 我喜欢这个,因为你可以通过给包装函数一个描述性名称(尽管可能不是getSomeResult)来获得一些可读性,太多其他答案并没有真正添加任何有价值的东西 (2认同)

Som*_*ken 9

1)我每次真的失去了SCE吗?编译器是否允许某些场景"优化它"并仍然提供SCE?

不,你没有,但它的应用方式不同:

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

在这里,OtherComplicatedFunctionCall()如果SomeComplicatedFunctionCall()返回true ,编译器甚至不会运行.

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

在这里,两个函数都将运行,因为它们必须存储在b1和中b2.b1 == true然后b2不会评估Ff (SCE).但OtherComplicatedFunctionCall()已经运行了.

如果b2在其他任何地方使用,编译器可能足够智能,如果函数没有可观察到的副作用,则内联if中的函数调用.

2)在这种情况下,最佳做法是什么?是否只有这种可能性(当我想要SCE时)才能直接拥有我需要的所有内容,并且"只是将其格式化为尽可能可读"?

那要看.您是否 OtherComplicatedFunctionCall()因为副作用而需要运行,或者该功能的性能损失很小,那么您应该使用第二种方法来实现可读性.否则,通过第一种方法坚持SCE.


lev*_*ime 8

短路的另一种可能性并且在一个地方具有条件:

bool (* conditions [])()= {&a, &b, ...}; // list of conditions
bool conditionsHold = true;
for(int i= 0; i < sizeOf(conditions); i ++){
     if (!conditions[i]()){;
         conditionsHold = false;
         break;
     }
}
//conditionsHold is true if all conditions were met, otherwise false
Run Code Online (Sandbox Code Playgroud)

您可以将循环放入函数中,让函数接受条件列表并输出布尔值.