g++:在涉及多个翻译单元的情况下,RVO是如何工作的
g++: How RVO works in case that multiple translation units are involved
首先请看下面的代码,它由两个翻译单元组成。
--- 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进行了上述实验。
编译器只需要始终如一地工作。
换句话说,编译器必须只查看返回类型,并根据该类型决定返回该类型对象的函数将如何返回值。
至少在一个典型的案例中,这个决定是相当微不足道的。它留出一个寄存器(或可能两个)用于返回值(例如,在通常为EAX或RAX的Intel/AMD x86/x64上)。任何小到可以放入的类型都将返回到那里。对于任何太大而无法容纳的类型,该函数将接收一个隐藏的指针/引用参数,该参数"告诉"它将返回结果存放在何处。请注意,这在完全不涉及RVO/NRVO的情况下适用——事实上,它同样适用于返回struct
的C代码,就像它适用于返回一个class
对象的C++一样。尽管返回struct
在C中可能不像在C++中那么常见,但它仍然是允许的,并且编译器必须能够编译这样做的代码。
实际上有两个单独的(可能的)副本可以消除。一种是编译器可以在堆栈上为保存返回值的本地文件分配空间,然后在返回期间从那里复制到指针引用的位置。
第二种可能是从该返回地址复制到值真正需要结束的其他位置。
第一个在函数本身内部被消除,但对其外部接口没有影响。它最终将数据放在隐藏指针告诉它的任何位置——唯一的问题是它是首先创建本地副本,还是总是直接使用返回点。显然,对于[N]RVO,它总是直接起作用。
第二个可能的拷贝是从那个(潜在的)临时拷贝到值真正需要结束的地方。这是通过优化调用序列而不是函数本身来消除的,即给函数一个指向返回值最终目的地的指针,而不是指向某个临时位置,编译器将从该位置将值复制到其目的地。
main
不需要getFoo
的实现细节就可以实现RVO。它只是希望在getFoo
退出后返回值在某个寄存器中。
getFoo
对此有两个选项-在其作用域中创建一个对象,然后将其复制(或移动)到返回寄存器,或者直接在该寄存器中创建对象。这就是发生的事情。
它并没有告诉main在其他地方查找,也不需要。它只是直接使用返回寄存器。
(N)RVO与翻译单位无关。该术语通常用于指两种不同的副本省略,一种可以在函数内部应用(从局部变量到返回值),另一种可以由调用方应用(从返回值到局部变量),它们应该单独讨论。
正确的RVO
这是严格在函数内部执行的,请考虑:
T foo() {
T local;
// operate on local
return local;
}
概念上有两个对象,local
和返回的对象。编译器可以对函数进行本地分析,并确定两个对象的生存期都是绑定的:local
的生存期只是作为返回值的副本的来源。然后编译器可以将两个变量绑定到一个变量中并使用它
在调用方复制省略
在主叫端,考虑T x = foo();
。还有两个对象,即从foo()
和x
返回的对象。编译器可以再次确定生存期是绑定的,并将两个对象放在同一位置。
进一步阅读:
值语义:NRVO
值语义:复制省略
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- Python中的for循环与C++有何不同
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?
- g++:在涉及多个翻译单元的情况下,RVO是如何工作的
- 函数返回的 RVO 和 rvalue 如何工作
- 为什么RVO不能与move构造函数一起工作
- C++11编译器何时会使RVO和NRVO优于移动语义和常量引用绑定