首先,请查看以下代码,其中包含2个翻译单元.
--- foo.h ---
class Foo
{
public:
Foo();
Foo(const Foo& rhs);
void print() const;
private:
std::string str_;
};
Foo getFoo();
--- foo.cpp ---
#include <iostream>
Foo::Foo() : str_("hello")
{
std::cout << "Default Ctor" << std::endl;
}
Foo::Foo(const Foo& rhs) : str_(rhs.str_)
{
std::cout << "Copy Ctor" << std::endl;
}
void Foo:print() const
{
std::cout << "print [" << str_ << "]" << std:endl;
}
Foo getFoo()
{
return Foo(); // Expecting RVO
}
--- main.cpp ---
#include "foo.h"
int main()
{
Foo foo = getFoo();
foo.print();
}
Run Code Online (Sandbox Code Playgroud)
请确保foo.cpp和main.cpp是不同的翻译单元.因此,根据我的理解,我们可以说翻译单元main.o(main.cpp)中没有getFoo()的实现细节.
但是,如果我们编译并执行上面的操作,我看不到"Copy Ctor"字符串,表明RVO在这里工作.
如果你们中的任何人都知道如何实现这一点,即使"getFoo()"的实现细节没有暴露给翻译单元main.o,我们将非常感激.
我使用GCC(g ++)4.4.6进行了上述实验.
Jer*_*fin 12
编译器必须始终如一地工作.
换句话说,编译器必须仅查看返回类型,并根据该类型决定返回该类型对象的函数将如何返回该值.
至少在典型的情况下,这个决定是相当微不足道的.它留出一个寄存器(或可能两个)用于返回值(例如,在通常为EAX或RAX的Intel/AMD x86/x64上).任何足够小的类型都将返回到那里.对于任何太大而不适合的类型,函数将接收一个隐藏的指针/引用参数,告诉它存放返回结果的位置.请注意,这很多都适用于RVO/NRVO根本不涉及 - 实际上,它同样适用于返回C代码的C代码,struct就像C++返回一个class对象一样.虽然返回一个struct可能在C中并不像在C++中那样常见,但它仍然是允许的,并且编译器必须能够编译执行它的代码.
实际上有两个独立的(可能的)副本可以被删除.一个是编译器可以在堆栈上为本地保留返回值的空间分配空间,然后从那里复制到指针在返回期间引用的位置.
第二个是从该返回地址到可能最终需要值的其他位置的可能副本.
第一个在函数本身内被消除,但对其外部接口没有影响.它最终将数据放在隐藏指针所指示的位置 - 唯一的问题是它是先创建本地副本,还是始终直接使用返回点.显然,对于[N] RVO,它总是直接起作用.
第二个可能的副本是从那个(潜在的)临时值到值真正需要结束的地方.这通过优化调用序列而不是函数本身来消除 - 即,给函数指向该返回值的最终目的地,而不是某个临时位置,然后编译器将该值复制到其目标中.
main不需要getFooRVO 的实现细节.它只是希望getFoo退出后返回值在某个寄存器中.
getFoo有两个选项 - 在其作用域中创建一个对象,然后将其复制(或移动)到返回寄存器,或直接在该寄存器中创建对象.这是怎么回事.
它并没有告诉主要看其他地方,也不需要.它只是直接使用返回寄存器.