考虑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()
的参数。为什么不是这样?
答案 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类型,通过将自动对象直接构造为函数的返回值,可以省略复制/移动操作
至于原因,它不是一个任意的规则,从实现的角度来看是有道理的,因为这是函数参数无法做到的事情:
将自动对象直接构造到函数的返回值
中
您正在复制功能参数。如果没有内联,则无法忽略此副本,因为在您输入函数之前参数已经存在,因此您无法直接将该对象构造为返回值。