RVO和移动语义

  • RVO (Return Value Optimization) :是关于函数返回值的优化。它能避免为函数返回的临时对象创建拷贝。
  • Pass-by-Value and Move :是关于函数参数传递的优化策略。它利用移动语义来接收参数,避免不必要的拷贝。

它们的应用场景完全不同,一个是"出",一个是"进"。下面我们来详细分解一下。


1. RVO/NRVO:函数返回时的优化

RVO (Return Value Optimization) 和 NRVO (Named Return Value Optimization) 是编译器的一种优化技术,它可以在函数返回一个对象时,直接将这个对象构造在调用者指定的内存位置上,从而完全省略掉一次拷贝(或移动)构造。

示例:

cpp 复制代码
std::string create_string() {
    std::string s = "hello world";
    return s; // 这里可能会发生 NRVO
}

int main() {
    std::string main_s = create_string();
}

没有 RVO/NRVO 的逻辑步骤:

  1. create_string 函数内部,创建一个局部 std::string s
  2. return s; 时,将 s 拷贝/移动到一个临时的返回值对象中。
  3. main 函数中,main_s 通过这个临时返回值对象进行拷贝/移动构造。

有 RVO/NRVO 的实际步骤:

  1. 编译器非常聪明,它发现 create_string 的返回值将要初始化 main_s
  2. 于是,编译器直接在 main_s 的内存空间上构造了那个值为 "hello world" 的字符串。
  3. 中间的局部变量 s 和临时返回值对象都"消失"了。没有发生任何拷贝或移动

关键点:RVO/NRVO 是一种"消除"操作,它让拷贝/移动从始至终都没有发生。从 C++17 开始,在某些情况下,这种优化已经是强制性的。


2. Pass-by-Value and Move:函数参数传递的优化

这是我们在 EventLoopThreadPool 构造函数中讨论的场景。我们来看一下这个构造函数:

cpp 复制代码
EventLoopThreadPool::EventLoopThreadPool(IEventLoop* baseLoop, std::string nameArg)
    : baseLoop_(baseLoop),
      name_(std::move(nameArg)), // 从参数移动到成员变量
      started_(false),
      numThreads_(0),
      next_(0)
{
}

这里我们分析两种情况:

情况 A:传递一个右值(临时对象)

当你这样调用时:
EventLoopThreadPool pool(loop, "my-pool");

这里的 "my-pool" 是一个 const char*,它会隐式地构造一个 std::string 的临时对象(这是一个右值)。

逻辑步骤:

  1. 一个 std::string 临时对象被创建。
  2. 这个临时对象被 移动构造 到参数 nameArg 中。(注意 :编译器通常会在这里进行一次类似 RVO 的优化,直接在 nameArg 的位置上构造临时对象,从而省略这次移动)。
  3. 在构造函数的初始化列表中,nameArg 通过 std::move 移动构造 到成员变量 name_ 中。

最终结果: 1 次构造 + 1 次移动。非常高效。

情况 B:传递一个左值(命名对象)

当你这样调用时:

cpp 复制代码
std::string pool_name = "my-pool";
EventLoopThreadPool pool(loop, pool_name);

这里的 pool_name 是一个左值。

逻辑步骤:

  1. pool_name拷贝构造 到参数 nameArg 中。因为我们不能修改 pool_name 本身。
  2. 在构造函数的初始化列表中,nameArg 通过 std::move 移动构造 到成员变量 name_ 中。

最终结果: 1 次拷贝 + 1 次移动。

关键点 :这种方式是通过 移动 来"窃取"参数 nameArg 的资源,而不是像 RVO 那样完全"消除"一个对象。它为处理左值和右值提供了一个统一且高效的接口。


总结与对比

特性 RVO / NRVO Pass-by-Value and Move
场景 (Context) 函数返回值时 函数参数传递时(特别是对于 "sink" 型参数,即要存起来的参数)
机制 (Mechanism) 消除 (Elision) 拷贝/移动,直接在调用者内存中构造对象。 通过移动 (Move) 来转移资源,避免深拷贝。
效果 理想情况下,没有额外的构造、拷贝或移动。 对于右值,有一次移动;对于左值,有一次拷贝和一次移动。
本质 编译器的"魔法",它改变了对象的构造地点。 C++11 语言特性(右值引用和移动语义)的一种编程模式。

当您传递一个右值(例如 "my-pool")时,它和 RVO 的目标都是避免昂贵的拷贝,但实现路径不同:

  • RVO 是在函数返回时,通过"消除"操作实现的。
  • 我们的构造函数参数优化,是在函数调用 时,通过移动语义来高效接收这个右值。
相关推荐
不知名用户来了2 分钟前
基于vue3 封装的antdv/element-Plus 快速生成增删改查页面
前端
明川8 分钟前
Android Gradle - ASM + AsmClassVisitorFactory插桩使用
android·前端·gradle
布列瑟农的星空9 分钟前
webpack迁移rsbuild——配置深度对比
前端
前端小黑屋11 分钟前
查看项目中无引用到的文件、函数
前端
前端小黑屋12 分钟前
小程序直播挂件Pendant问题
前端·微信小程序·直播
flashlight_hi14 分钟前
LeetCode 分类刷题:199. 二叉树的右视图
javascript·算法·leetcode
俊男无期16 分钟前
超效率工作法
java·前端·数据库
LYFlied16 分钟前
【每日算法】LeetCode 46. 全排列
前端·算法·leetcode·面试·职场和发展
2301_8234380217 分钟前
【无标题】解析《采用非对称自玩实现强健多机器人群集的深度强化学习方法》
数据库·人工智能·算法
oscar99919 分钟前
CSP-J教程——第二阶段第十二、十三课:排序与查找算法
数据结构·算法·排序算法