首先请看下面的代码,它由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 是不同的翻译单元。因此,根据我的理解,我们可以说翻译单元 main.o (main.cpp) 中没有可用的 getFoo() 实现细节。
然而,如果我们编译并执行上面的代码,我看不到“Copy Ctor”字符串,这表明 RVO 在这里工作。
如果你们中的任何人请让我知道即使“getFoo()”的实现细节没有暴露给翻译单元 main.o,如何实现这一点,我将不胜感激。
我使用GCC(g++)4.4.6进行了上述实验。
编译器只需一致地工作即可。
换句话说,编译器必须只查看返回值type,并根据该类型,决定返回该类型对象的函数如何返回该值。
至少在典型情况下,该决定是fairly琐碎的。它预留一个寄存器(或可能两个)用于返回值(例如,在 Intel/AMD x86/x64 上,通常为 EAX 或 RAX)。任何足够小的类型都将返回那里。对于任何太大而无法容纳的类型,该函数将接收一个隐藏的指针/引用参数,告诉它将返回结果存放在哪里。请注意,这在完全不涉及 RVO/NRVO 的情况下也适用——事实上,它同样适用于返回 a 的 C 代码struct
就像 C++ 返回一个class
目的。虽然返回一个struct
在 C 中可能不像在 C++ 中那么常见,但它仍然是允许的,并且编译器必须能够编译执行此操作的代码。
实际上有两个单独的(可能的)副本可以被消除。一是编译器可能会在堆栈上为本地保存返回值分配空间,然后从那里复制到返回期间指针引用的位置。
第二个可能是从该返回地址复制到该值真正需要结束的其他位置。
第一个在函数本身内部被消除,但对其外部接口没有影响。它最终将数据放置在隐藏指针告诉它的位置——唯一的问题是它是否首先创建本地副本,或者总是直接使用返回点。显然,使用 [N]RVO,它总是直接起作用。
第二种可能的复制是从(潜在的)临时复制到值真正需要结束的地方。这是通过优化调用序列而不是函数本身来消除的——即,为函数提供一个指向该返回值的最终目的地的指针,而不是某个临时位置,编译器将从该临时位置将值复制到其目的地。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)