1

考虑一下测试代码:

#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)

我期待编译器使用 RVO 机制来省略每个 COPY CTOR 调用,以避免复制函数的返回值和参数create()。为什么不是这样?

4

2 回答 2

3

该标准仅在您将临时参数作为函数参数传递的情况下才允许复制省略。

您期望的两个省略号在下面以粗体显示:

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

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

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

构造 into 的参数已经发生了,否则create你会看到:

Klass()
Klass(const Klass& right)
create(Klass a)
Klass(const Klass& right)
于 2012-02-15T13:03:14.460 回答
2

您看到的副本是“create”函数中“return”语句的副本。它不能被 RVO 消除,因为不可能直接构造返回值。您要求“退货”。这里需要一份副本;没有它就无法返回对象。

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

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

As for the reasons, it is not an arbitrary rule, it makes sense from implementation point of view, as this is what is not possible to do with a function parameters:

constructing the automatic object directly into the function’s return value

You are copying the function parameter. You cannot elide this copy without inlining, as the parameter already exists before you enter the function, therefore you cannot construct that object into the return value directly instead.

于 2012-02-15T13:06:00.457 回答