为什么g ++不在这里启用RVO?

时间:2012-02-15 12:50:35

标签: c++ g++ rvo

考虑TEST代码:

#include <iostream>
using namespace std;

class Klass
{
public:
  Klass()
  {
    cout << "Klass()" << endl;
  }

  Klass(const Klass& right)
  {
    cout << "Klass(const Klass& right)" << endl;
  }
};

Klass create(Klass a)
{
  cout << "create(Klass a)" << endl;
  return a;
}

int main()
{
  const Klass result = create(Klass());
}

编译:

g++ -O3 rvo.cpp   -o rvo

输出结果为:

$ ./rvo
Klass()
create(Klass a)
Klass(const Klass& right)

我希望编译器在每次COPY CTOR调用时都使用RVO机制,以避免复制返回值和函数create()的参数。为什么不是这样?

2 个答案:

答案 0 :(得分:3)

只有在您将临时函数作为函数参数传递时,该标准才允许复制省略。

你期望的两个分支在下面加粗:

  

[C++11: 12.8/31]:当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间。没有优化就被破坏了。 在以下情况下(可以合并以消除多份副本),允许复制/移动操作的省略(称为复制省略):

     
      
  • 在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称时(除了函数或catch子句参数)使用与函数返回类型相同的cv-unqualified类型,可以通过将自动对象直接构造为函数的返回值来省略复制/移动操作
  •   
  • 在throw-expression中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会超出最内层封闭try-block的末尾(如果有一个),通过直接将自动对象构造成异常对象,可以省略从操作数到异常对象(15.1)的复制/移动操作
  •   
  • 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unqualified类型的类对象时,可以通过构造它来省略复制/移动操作临时对象直接进入省略的复制/移动目标
  •   
  • 当异常处理程序的异常声明(第15条)声明一个相同类型的对象(cv-qualification除外)作为异常对象(15.1)时,可以通过处理异常来省略复制/移动操作-declaration作为异常对象的别名,如果程序的含义将保持不变,除了执行异常声明声明的对象的构造函数和析构函数。 [..]
  •   

返回值没有发生,因为非易失性名称是函数参数。

已经构造成create的参数,否则你会看到:

Klass()
Klass(const Klass& right)
create(Klass a)
Klass(const Klass& right)

答案 1 :(得分:2)

您看到的副本是“创建”功能中“return”语句的副本。它不能被RVO消除,因为不可能直接构造返回值。你要求“退回”。这里需要一份副本;没有它就没有办法返回一个物体。

在标准说法中,不满足[C ++ 11:12.8 / 31]的以下条件

  

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

至于原因,它不是一个任意的规则,从实现的角度来看是有道理的,因为这是函数参数无法做到的事情:

  

将自动对象直接构造到函数的返回值

您正在复制功能参数。如果没有内联,则无法忽略此副本,因为在您输入函数之前参数已经存在,因此您无法直接将该对象构造为返回值。