c++ RVO 和 NRVO

本文探讨了C++中RVO(Return Value Optimization)与NRVO(Named Return Value Optimization)的概念及其实现细节。通过示例代码展示了不同编译器环境下RVO与NRVO的工作原理及其对性能的影响。

在查看std::move是否有必要在return localvar 中使用的时候,查看到了RVO和NRVO,因为std::move总是会调用移动构造函数所以会增加一次构造。

关于RVO和NRVO

RVO (return value optimization) 和NRVO (named return value optimization) 是C++在处理 “返回一个class object的函数” 时常用的优化技术,主要作用就是消除临时对象的构造和析构成本

经过测试,gcc编译器在debug和release模式下均支持NRVO,VS在debug模式下不支持NRVO,仅支持RVO,而在release模式下也支持NRVO

参考文章 蓝大@蓝色的这篇文章

RVO V.S. std::move 讲解的很到位。

C++的拷贝构造函数的调用时机? - Skillness 的回答 

Return value optimization 维基百科

首先,关于C++标准中的这段话,在copy elision中找到的关于RVO的描述如下

  • When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".

翻译一下:

当一个未命名且未绑定到任何引用的临时变量被移动或复制到一个相同的对象时,拷贝和移动构造可以被省略。当这个临时对象在被构造的时候,他会直接被构造在将要拷贝/移动到的对象。当未命名临时对象是函数返回值时,发生的省略拷贝的行为被称为RVO,"返回值优化"。

代码:
#include <iostream>
using namespace std;
struct C {
	C() { std::cout << "A Construct.\n"; }
	~C(){ std::cout << "A DisConstruct.\n"; }
	C(const C&) { std::cout << "A copy was made.\n"; }
	//C(C&&) { std::cout << "A move was made.\n"; }
};
template<typename T>
T f() {
	T c;
	//return std::move(c);
	return c;
}
int main() {
	std::cout << "Hello World!\n";
	C obj = f<C>();
	//f<C>();
	return 0;
}

在vs debug(RVO)中输出:


在vs release中输出:


在linux g++下输出


不使用RVO和NRVO的结果:


那么RVO编译器是怎么优化的呢

T f() {
	T c;
	return c;
}

void f(T& _c) {
	T c;
        _c.T::T(c);//拷贝构造调用
	return ;
}
主函数:

C obj = f<C>();
C obj;//定义而不构造
(f<C>(obj),obj);

显然如果调用方式为f<C>(),不赋值的话RVO没有用

而NRVO的优化,即直接用临时对象的引用替换了原本的返回变量,并在函数体内调用普通构造函数进行构造

void f(T& _c) {
	_c.T::T();//优化拷贝构造
	return ;
}






<think>好,我现在需要回答关于C++中函数返回大对象时,调用者预留空间以及RVONRVO优化的相关问题。首先,我得理解用户的问题。他们想知道当函数返回大对象时,如何通过RVONRVO优化来避免不必要的拷贝,以及这些优化的实现方法。 首先,我应该从RVONRVO的定义开始。RVO是返回值优化,当函数返回一个临时对象的时候,编译器会直接在调用者的栈帧上构造这个对象,避免复制。而NRVO是命名返回值优化,适用于函数内部有命名变量作为返回值的情况,同样避免拷贝。 接下来,我需要解释这些优化是如何工作的。比如,当函数返回一个临时对象时,编译器可以识别这种情况,直接将对象构造到调用者预留的空间中。对于NRVO,即使返回值是一个局部变量,编译器也会优化,避免额外的拷贝操作。然后,我需要给出示例代码,说明在有没有优化的情况下代码的不同表现。还要提到移动语义,这是C++11引入的特性,当RVO/NRVO无法应用时,比如返回的局部变量被转换为右值,这时会调用移动构造函数而不是拷贝构造函数,从而提高效率。然后,我需要讨论如何确保这些优化生效。比如,避免返回多个不同的对象,保持返回路径简单,确保返回值类型支持移动或拷贝构造等。 最后,在相关问题部分,需要提出与RVONRVO相关的问题,比如它们的区别、移动语义的作用、禁用优化的情况、手动确保优化的方法等。确保问题覆盖用户可能想进一步了解的方面。 在检查回答的时候,要确保解释清晰,代码示例正确,并且相关问题具有连贯性扩展性,帮助用户深入理解主题。</think>在C++中,当函数返回大对象时,可以通过返回值优化(RVO命名返回值优化(NRVO)来避免不必要的拷贝。这些优化允许编译器直接在调用者的内存空间构造对象,而不是在函数内部构造后复制。 以下是具体实现机制示例代码: 1. **RVO(Return Value Optimization)** ```cpp struct BigObject { int data[1000]; BigObject() { std::cout << "Constructed\n"; } BigObject(const BigObject&) { std::cout << "Copied\n"; } }; BigObject createRVO() { return BigObject(); // 直接构造到调用者的内存空间 } int main() { BigObject obj = createRVO(); // 无拷贝构造调用 } ``` 2. **NRVO(Named Return Value Optimization)** ```cpp BigObject createNRVO() { BigObject local_obj; // 命名对象 return local_obj; // 可能直接使用调用者的内存空间 } int main() { BigObject obj = createNRVO(); // 可能无拷贝构造调用 } ``` 3. **移动语义支持** ```cpp BigObject createWithMove() { BigObject local_obj; return std::move(local_obj); // 强制使用移动语义 } ``` **优化原理**: - 编译器会在调用栈上为返回对象预留空间 - 函数内部直接在预留地址构造对象 - 避免调用拷贝构造函数 - 需要满足特定条件(返回局部对象/临时对象) **优化触发条件**: 1. 返回语句直接构造临时对象(RVO) 2. 返回语句使用函数内的局部变量(NRVO) 3. 所有返回路径返回同一个局部对象 4. 对象类型与返回值类型严格匹配 **最佳实践**: 1. 保持简单的返回路径 2. 避免返回全局/静态变量 3. 优先返回局部对象而非参数 4. 为类实现移动语义作为优化失败时的保障
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值