在c++; 让我们假设有一个C++函数FoE(),它返回一个布尔值。

在c++; 让我们假设有一个C++函数FoE(),它返回一个布尔值。,c++,optimization,compiler-construction,C++,Optimization,Compiler Construction,我调用此函数来检查属性的状态 或获取函数调用的结果 那么,调用这类函数的最佳方式是什么呢 方法1: bool flag = foo() if (flag) { // some code } else { // else some code } 方法2: if ( foo() ) { // some code } else { // some code } 我的问题:使用临时变量是否会给编译器提供更好的优化机会。优化器将生成相同的结果。以可读、可理解的代码为目标。我通常

我调用此函数来检查属性的状态 或获取函数调用的结果

那么,调用这类函数的最佳方式是什么呢

方法1:

bool flag = foo()
if (flag)
{
   // some code
}
else
{
   // else some code
}
方法2:

if ( foo() )
{
   // some code
}
else
{
   // some code
}

我的问题:使用临时变量是否会给编译器提供更好的优化机会。

优化器将生成相同的结果。以可读、可理解的代码为目标。我通常更喜欢第一个选项,因为它更容易调试(您可以在调试器中看到返回的值)。

对于基本变量,没有太大区别。由于优化,它们都会导致生成相同的机器代码。在第一种情况下,结果也是可重用的


但是,如果返回的类型是具有析构函数的对象。然后,出现了一种不同。对于第一种情况,对象将被销毁,直到退出其作用域(例如,在您的情况下退出函数),对于第二种情况,对象将在
if
语句中求值后立即销毁

这取决于具体情况:如果您多次使用该值(andy只需调用foo一次),或者如果您可以为变量指定一个非常有意义的名称,那么您可能应该这样做。如果这些都不是真的,那就是风格/偏好的问题

示例(1):

示例(2):


我更喜欢第一种稍作改动的方法:

const bool flag = foo();  // const myclass &cref = foo_returning_object();
if (flag)
{
   // some code
}
else
{
   // else some code
}
在这种情况下,如果需要,我可以重用返回的值。若我的临时引用是常量引用,那个么它会延长返回对象的生存期,并且不会对我的程序施加任何开销(构造函数/析构函数调用)


与其他post答案一样,调试返回值更容易。

我通常选择第二个,除非我需要重用该标志。的确,在某些情况下,它可能对调试有用,但我不喜欢用编译器能够自行处理的临时变量污染代码


一种解决方法是将所有方法1放在一个块中,以便在if语句之后释放标志。没有结果的额外工作:我仍然选择方法2。

首先,让我们记住,编译器对源代码的看法与我们不同:语法对它不重要,只有语义。因此,我们可以说,它对代码有着相当象征性的看法

此外,当优化器发挥作用时,它将应用分析和转换,将等效符号执行转换为等效二进制

因此,优化的关键是语义。有3种不同的语法形式可用于
if
语句:

// with a temporary
if (make()) { ... } else { ... }

// with a variable already in scope
auto var = make();
if (var) { ... } else { ... }

// with a new variable
if (auto var = make()) { ... } else { ... }
后一种语法被分解为:

{ auto var = make(); if (var) { ... } else { ... } }
语义上存在差异:这些变量的范围不同

  • 在临时情况下,其生存期在执行
    if
    else
    块之前结束
  • 在命名变量的情况下,它的生存期在其作用域结束时结束,在
    else
    之后
一般来说,从语义上讲,寿命的差异应该可以忽略不计。但在某些情况下,这可能很重要:您是否需要访问它,或者副作用是否需要正确排序

可能会对发出的代码产生一些影响:

  • 对于内置类型:不太可能有任何影响
  • 对于吊舱类型:它可能没有被优化得那么清楚
  • 对于非POD类型:析构函数中的副作用(如释放内存)可能会抑制优化
哪些优化

好的,这里可以应用的主要优化是堆栈重用,每当一个对象的生命周期结束时,编译器应该可以自由地为另一个对象重用其内存空间(请参阅)


如果编译器无法证明该对象已不再使用,或者由于其析构函数中的副作用而必须使其保持活动状态,那么它可能必须为此对象在堆栈上占用更多空间。如前所述,对于内置类型,这种情况不太可能发生(启用了优化),但对于
std::string
,不幸的是,我不会感到惊讶。

编译器可能在这两种情况下做完全相同的事情。在测试foo之前,它仍然必须使用一个临时文件来存储foo的结果。@gandaliter很可能,编译器将返回寄存器中的
bool
,并直接对其进行测试。如果没有优化,我希望第一个版本也会在堆栈上产生一个额外的变量,但选择第二个形式的真正原因是它更可读,更容易理解。Hmmm。我使用的两个调试器(VS和gdb)都将显示函数返回的值,因此没有参数。而第一个版本显然可读性较差,因为它在函数中引入了额外的噪声。@JamesKanze(不查看注册表)如何在VS中看到值?我真的不知道,这对我有很大帮助。在VS2013中,它出现在局部变量窗口的顶部。(在早期版本中,我确实觉得这有点随机。这也是我强烈喜欢gdb的原因之一。但他们现在似乎已经修复了这一部分。)@JamesKanze啊,现在不要使用13:(同意。如果该值在一个非常简单的表达式中只使用一次,那么声明变量就是额外的噪音,这会降低函数的可读性。如果该值被多次使用,当然,请将其放入变量中。如果函数调用和使用它的表达式都很复杂,则使用命名变量可能会使事情变得复杂。)更精简。但任何好的调试器都会显示函数的返回值,因此该参数是一个wash。通常,状态越少,就越容易对函数进行推理。我认为您的意思是“它延长了返回对象的生存期”(而不是“它延长了
// with a temporary
if (make()) { ... } else { ... }

// with a variable already in scope
auto var = make();
if (var) { ... } else { ... }

// with a new variable
if (auto var = make()) { ... } else { ... }
{ auto var = make(); if (var) { ... } else { ... } }