首先请看看下面的代码,其中包含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();
}
请确保Foo.cpp中和main.cpp中是不同的翻译单元。 所以按我的理解,我们可以说,存在的getFoo没有实施细则()在翻译单元main.o(main.cpp中)可用。
但是,如果我们编译和执行上面的,我无法看到“复制构造函数”字符串这表明RVO在这里工作。
这将是非常感激,如果有人对你请让我知道该怎么就算“的getFoo()”的实施细则未暴露于翻译单元main.o可以做到这一点?
我通过使用GCC(克++)4.4.6进行上述实验。
编译器只是有工作始终。
换句话说,编译器必须在返回类型单独看,并基于该类型,决定如何返回该类型的对象的函数将返回值。
至少在一般情况下,这个决定是相当微不足道。 它留出寄存器(或可能是两个)用于返回值(例如,在Intel / AMD的x86 / x64的那会通常是EAX或RAX)。 任何类型的足够小,以适应将要返回那里。 对于任何类型的太大,不适合在那里,该函数将接收告诉它在哪里存放返回结果的隐藏指针/引用参数。 请注意,这多不适用RVO / NRVO被卷入的话-事实上,它同样适用于C代码,返回一个struct
,它确实给C ++返回一个class
的对象。 虽然返回一个struct
用C可能是不太一样共同的象在C ++中,它仍然允许的,编译器必须能够编译代码,不会吧。
有真的可以消除两个独立的(可能的)副本。 一个是编译器可以在栈上分配空间的局部保持会是怎样的返回值,然后从那里复制到返回时的指针指向。
二是从返回地址可能复制到一些其他地方,价值真正需要的结束。
第一个被淘汰的功能本身里面,而是有其外部接口上没有影响。 它最终使地方隐藏的指针告诉它的数据 - 唯一的问题是它是否首先创建一个本地副本,或者总是直接返回点工作。 显然,用[N] RVO,它总是直接运行。
第二个可能的副本是从(潜在的)临时到哪里的价值真正需要的结束。 这是通过优化调用序列消除,而不是函数本身 - 即给函数的指针最终目的地为返回值,而不是一些临时位置,从该编译器会那么该值复制到它的目的地。
main
并不需要实施细则getFoo
的RVO发生。 它只是预期的返回值是在经过一番寄存器getFoo
退出。
getFoo
有这两个选择-创造在其范围内的对象,然后复制(或移动)到返回寄存器,或直接在寄存器中创建对象 。 这是发生了什么。
这不是告诉主看别的地方,也不需要。 它只是直接使用返回寄存器。
(N)是RVO无关的翻译单元。 该术语通常用于指的是可应用的一个功能内(从一个局部变量来返回值),并通过呼叫方(从返回值到本地变量)两个不同的副本省音,并且它们应讨论分别。
附近RVO
这是一个函数内部严格履行,可以考虑:
T foo() {
T local;
// operate on local
return local;
}
从概念上有两个对象, local
和返回的对象。 编译器可以在本地分析功能,并确定两个对象的生命周期势必: local
只活作为副本的返回值的源。 然后,编译器可以在单个可变结合两个变量,并使用它。
在发送方复制省略
在主叫侧,考虑T x = foo();
。 再次存在两个对象,返回的对象从foo()
和x
。 又一次的编译器可以判断寿命绑定,并放置在同一位置的两个对象。
进一步阅读: