3

原始代码

#include <iostream>
int global;
struct A
{
   A(){}
   A(const A&x){
       ++global;
   }
   ~A(){}
};
A foo()
{  
     A a;
     return a;  
}
int main()
{
   A x = foo();
   std::cout << global;
}

输出将0在支持命名返回值优化的优化编译器上。

当我将定义更改foo

A foo()
{ 
  { 
     A a;
     return a;  
  }
}

我得到1作为输出,即复制 c-tor 被调用一次。可能的原因是什么?引入虚拟作用域会完全改变代码的行为。我错过了什么?

我在 g++ 编译器上对其进行了测试。这里有任何编译器人员可以以某种特定于实现的方式解释该场景吗?

编辑

我在 clang 上对其进行了测试,即使在第二种情况下,它也优化了对复制 c-tor 的调用。

Andrew Pinski(gcc 专家)证实这确实是 g++ 错过优化的一个案例。

4

5 回答 5

3

除了编译器不够聪明,无法看到虚拟范围(由额外的括号引入)没有任何区别之外,我没有看到任何其他原因,至少对于这个特定的代码而言。编译器被多余的括号愚弄了;它可能对函数体的其余部分(甚至不存在)做出了疯狂的假设。

零或 1,无论哪种方式,行为都完全符合标准,因为标准不需要编译器生成0(或1就此而言)。如您所知,这取决于编译器。

至于foo两种情况下生成的汇编代码只有一点点区别:

  • 第一个代码:

    __Z3foov:
    LFB992:
         .cfi_startproc
         movl  4(%esp), %eax
         ret   $4
         .cfi_endproc
    
  • 第二个代码:

    __Z3foov:
    LFB992:
         .cfi_startproc
         incl _global      <----- incrementing the global. God knows why!
         movl  4(%esp), %eax
         ret   $4
         .cfi_endproc
    

我用过g++ -O6。版本 :MinGW (GCC) 4.6.1

于 2011-12-15T15:51:38.087 回答
2

理论上你可以在这两种情况下得到 1。这是编译器可能会或可能不会优化复制构造函数的情况之一。

您可以通过谷歌搜索"Return value optimization""Named return value optimization"找到有关该主题的更多详细信息,在您的情况下是后者。

请注意,如果您将代码更改为:

A foo()
{ 
  { 
     return A();
  }
}

然后 RVO 应该启动,您将在输出中获得 0。

在你描述的情况下,为什么 NRVO 没有介入?(我已经在 GCC 4.6 上确认了这一点。)我现在不确定;要么编译器不够聪明,要么有一条关于 NRVO 的规则在这里不允许它。


编辑:

标准说...

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。(...) 这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的:

— 在具有类返回类型的函数的 return 语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作

因此在这里是允许的,但是编译器不够聪明,无法在这里执行 NRVO。如果你在 GCC 上工作,你可以检查它在 Clang 上是否相同,看看结果是否不同(我的直觉说会)。

请注意,RVO 和 NRVO 是编译器功能的名称,而“复制省略”是标准通常引用此行为的方式。

于 2011-12-15T15:38:40.927 回答
2

用哪个编译器?这两个结果都是合法的,所以正式,你不能抱怨。实际上,我不明白为什么引入范围会改变任何东西。作为实施质量问题,我认为您可以投诉。

于 2011-12-15T15:48:16.410 回答
0

编译器可能会错过某些情况,即使在其他情况下进行优化也是可能的。AFAIK——我现在检查了允许复制构造函数省略的地方——没有约束变量必须在函数的顶部范围内,它必须只是一个自动变量。

于 2011-12-15T15:40:07.180 回答
-5

当您按值返回时,将有一个副本。优化设置可以(应该)永远不会改变程序的可见行为。

于 2011-12-15T15:36:30.793 回答