C++返回值拷贝以及std::move

C++拷贝省略:编译器隐形优化与移动语义的协同实践 在C++编程中,对象拷贝是常见的性能瓶颈,尤其在处理大型数据结构或高频调用时。编译器通过拷贝省略技术,在满足as-if规则的前提下消除不必要的拷贝或移动操作,直接从源位置构造目标对象,实现零成本抽象。这项优化技术是现代C++高效值传递的基石,与C++11引入的移动语义协同工作,显著提升程序性能。在C++17标准中,对纯右值初始化的场景强制要求拷贝省略,使得返回临时对象的操作更加高效安全。理解RVO与NRVO的区别至关重要:RVO针对返回临时对象,C++17后强制优化;NRVO针对返回命名局部对象,是编译器可 阅读详情

最近在看《effective modern C++》,到条款二十五,看到了C++返回值拷贝的说明,故做一下总结。并参考了网上的资料。深入理解C++中的RVO

概述

我将以下面的类为例子,说明C++在RVO的情况下以及关闭RVO情况下,其函数返回值是如何返回给调用者的,并给出其汇编代码的说明。环境是Ubuntu Linux,编译器为GCC7.5.0。

class Obj {
public:
    Obj() { // 构造函数
    std::cout << "in Obj() " << " " << this << std::endl;
    }
    Obj(int n) {
    std::cout << "in Obj(int) " << " " << this << std::endl;
    }
    Obj(const Obj &obj) { // 拷贝构造函数
    std::cout << "in Obj(const Obj &obj) " << &obj << " " << this << std::endl;
    }
    Obj(const Obj &&obj) { // 移动构造函数
    std::cout << "in Obj(const Obj &&obj) " << &obj << " " << this << std::endl;
    }
    Obj &operator=(const Obj &obj) { // 赋值构造函数
    std::cout << "in operator=(const Obj &obj)" << std::endl;
    return *this;
    }
    Obj &operator=(const Obj &&obj) { // 移动赋值构造函数
    std::cout << "in operator=(const Obj &&obj)" << std::endl;
    return *this;
    }
    ~Obj() { // 析构函数
    std::cout << "in ~Obj() " << this << std::endl;
    }
private:
    int n;
};

关闭RVO

编译命令加上-fno-elide-constructors表示关闭RVO 优化开关。定义函数fun1,并调用。

//test28_RVO.cpp
Obj fun1() {
  Obj obj;
  // do sth;
  return obj;
}

int main() {
    Obj a = fun1();
    return 0;
}

编译:g++ -fno-elide-constructors -g -o test28_RVO test28_RVO.cpp。
结果打印:如下图所示。

  • 定义拷贝构造函数以及移动构造函数的情况下:
    在这里插入图片描述

  • 定义拷贝构造函数而未定义移动构造函数的情况下:
    在这里插入图片描述

结果分析:从打印可以分析出,在关闭RVO优化的情况下,C++返回一个对象会调用拷贝构造函数(或者移动构造函数)2次。分析其汇编代码,可知,在main函数调用函数fun1()的时候,调用者会将一个栈的地址传递给fun1()。在函数fun1()内,当调用构造函数创建obj之后,会将obj拷贝到这个栈的地址,作为一个临时对象。函数fun()调用结束之后,会再调用一次拷贝构造函数,临时对象拷贝到接收者即a中。
在这里插入图片描述

在RVO下

代码和上面的相同。

//test28_RVO.cpp
Obj fun1() {
  Obj obj;
  // do sth;
  return obj;
}

int main() {
    Obj a = fun1();
    return 0;
}

编译:g++ -g -o test28_RVO test28_RVO.cpp。
结果打印:如下图所示。
在这里插入图片描述

结果分析:在启用RVO优化的情况下,只调用了一次构造函数,拷贝构造函数没有被调用。
从汇编代码结果可以看出,main函数将地址-0x1c(%rbp)传递给函数fun1(),之后,fun1()在调用Obj构造函数的时候,直接在这个地址上进行构造,免去了临时对象的创建以及拷贝。
在这里插入图片描述

RVO的条件

《modern effective c++》中指出,在开启RVO之后,返回局部对象只有在满足以下两个条件的情况下,编译器才会进行RVO优化。
(1)局部对象与函数返回值的类型相同。
(2)局部对象就是要返回的东西。函数形参不满足要求。
注意:当函数不同控制路径返回不同局部变量时,不会进行拷贝消除的操作,因为其不知道要返回哪个对象。

返回值为std::move的情况

代码如下:

Obj fun2() {
  Obj obj;
  // do sth;
  return std::move(obj);
}

int main() {
    Obj a = fun2();
    return 0;
}

编译:g++ -g -o test28_RVO test28_RVO.cpp。
结果打印:如下图所示

  • 定义拷贝构造函数以及移动构造函数的情况下:
    在这里插入图片描述

  • 定义拷贝构造函数,未移动构造函数的情况下(之所以编译器未报错,是因为const修饰的左值形参能够接收一个右值实参):
    在这里插入图片描述

结果分析:从汇编代码中可以看出,main将变量a的地址传递给fun2()。fun2()创建Obj obj,然后调用移动构造函数将obj移动到a中。
在这里插入图片描述

C++ std::move 用法示例 头文件中,它是一个模板函数,接受一个参数并返回该参数的右值引用。它的作用是告诉编译器,我们希望将对象转为右值,以便进行移动语义的操作。是一个非常有用的函数,它用于将对象转移给其他地方,通常是用于实现高效的移动语义。这表明我们的对象首先通过默认构造函数进行了创建,然后通过移动构造函数进行了移动操作。由于返回值使用了移动语义,编译器会进行返回值优化,避免了拷贝构造函数的调用。这表明我们的对象首先通过默认构造函数进行了创建,然后通过移动构造函数进行了移动操作。在上面的示例中,我们定义了一个名为。 阅读详情

相关推荐

std::move功能

容器操作:当向容器中插入元素时,如果元素类型支持移动语义, std::move 可以将元素移动到容器中,避免拷贝。- 函数返回值:在函数返回对象时,可以使用 std::move 将局部对象的资源移动到函数外部,而不是进行拷贝。std::move 是C++ 中的一个函数,其功能是将对象的资源所有权进行转移,以实现移动语义,减少不必要的拷贝,提高程序性能。在上述代码中,通过 std::move 将 str 移动到 vec 中, str 的资源被转移,其内容变为空字符串。// 输出vector中的字符串。

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 448

c++11的函数返回值要不要std::move()

比较返回一个对象,到底该怎么写? // 类型一 std::string getTimeNowString() { string str = "2010-05-04"; return str; } // 类型二 std::string getTimeNowString() { string str = "2010-05-04"; return std::move(str); } // 类型三 std::string& getTimeNowString() {

robinfoxnan的专栏 2763

【生成数据集EXCEL文件】使用生成对抗网络GAN生成数据集:输出生成数据集EXCEL

生成器的任务是接收随机噪声向量作为输入,并尽可能生成与真实数据相似的样本,而判别器则是一个二分类器,旨在区分输入的样本是来自生成器生成的,还是来自真实数据。在训练过程中,生成器和判别器通过对抗学习的方式相互博弈,生成器的目标是生成足以欺骗判别器的样本,而判别器的目标则是尽可能准确地判断样本的真实性。GAN网络在多个领域展现了强大的能力,能够生成与训练数据分布相似的新数据,包括但不限于图像生成、图像风格转换、超分辨率、数据增强、视频生成、自然语言处理、医学图像处理以及游戏与虚拟现实等。

hustef的博客 1521

一文讲透c++中std::move

std::move是C++11提供的一个函数模板,它可以将一个左值转换为一个右值引用,从而可以触发移动语义。使用std::move可以提高性能和避免不必要的拷贝。但是也要注意不要滥用std::move,因为它可能会导致一些问题:使用std::move后的对象可能会处于一种未定义或者无效的状态,不能再进行正常的操作。使用std::move可能会阻止编译器进行一些优化,比如返回值优化。使用std::move可能会破坏类的封装性和异常安全性。

行走的漂流瓶的博客 2922

u3d逆向学习之纯C内存读写与ImGui绘制(Android)

方法:需要在/proc/pid/maps文件查找模块名,一行行遍历所有内存,第一个参数为模块名,第二个参数为索引index(因为该模块会有多个,index为正序遍历第index个该模块),若找到了则使用。32位游戏的指针大小采用4字节,而64位游戏的指针大小采用8字节(这里需要注意),数据地址以16进制存储,使用unsigned long类型 ,该函数只有一个参数,数据地址,返回该地址的指针跳转地址。文件,比较其中的内容是否与传入的包名相同,如果相同,则返回该进程的PID。

zhoujunjie2004的博客 1687

Vivado安装教程:多版本共存的配置技巧

详解Vivado安装教程中的多版本共存配置技巧,帮助用户在同一系统中管理不同版本的Vivado开发环境,提升开发效率与兼容性,适用于需要切换版本的FPGA开发者。

weixin_35257663的博客 1777

什么情况下需要在 C++ 中使用 return std::move(X)

介绍一下 C++ 中 return std::move 的使用场景。

Kovnt的博客 2013

Buck电源设计常见的一些问题(四)MOS管振荡抑制方法(二)

另一方面,开关振铃为远超开关频率的高频振铃,并伴随很高的 dV/dt,会带来传导和辐射的 EMI 问题,可能会使得终端产品不能通过 EMI 标准测试,更严重时甚至会干扰开关电源自身的信号电路或临近的其他功能电路的正常工作。选用二极管反向特性好的器件;该电路可等效成图 3 中 LC 谐振电路,新的 LR和 CR为 Lp1、Lp5,Lp3, Lp4, CD等所有参与振铃的寄生感容的复合值。通常 Snubber 的 Csnub取值在数 nF 以上,在振铃频率 fR(加了 snubber 后)下的阻抗很小。

小幽余生不加糖 2892

【C++11】分析return std::move返回局部对象的必要性

在返回局部对象时使用std::move也许是不必要的,因为编译器会自动应用返回值优化(RVO),所以return std::move到底有没有必要呢

xy286285810的博客 1353

CAN 通信测试工具 canutils

概述 CAN,Controller Area Network,控制器局域网,一种高可靠性的现场总线。广泛用于汽车电子、工业控制通信。 canutils 工具包内含 5 个独立的程序:canconfig、candump、canecho、cansend、cansequence。这几个程序的功能简述如下: canconfig:用于配置 CAN 总线接口的参数,主要是波特率和模式。 candump...

一个软件工程师的技术笔记 1万+

8.DJI-PSDK:一站式项目功能开发总结(空中气象站项目/激光甲烷检测项目)

本文将以一篇文章的幅度快速完成: 项目一:空中气象站项目; 项目二:激光甲烷项目; 项目所使用的PSDK来源于第0章节末尾的百度网盘;解压:Payload-SDK-3.8.zip代码所在目录:双击打开: 修改打印串口和相关波特率,我们修改如下: 进行相关编译选项配置如下: :

哒宰的博客 294

函数返回使用std::move()避免复制

vector&lt;string&gt; fun() { vector&lt;string&gt; str; str.emplace_back("aaaaaaaaaaaaaaaaaaa"); str.emplace_back("aaaaaaaaaaaaaaaaaaa"); str.emplace_back("aaaaaaaaaaaaaaaaaaa"); ...

闲人的专栏 6092

CQE和SQE 以及 PQA和DQA的区别

‌:PQA聚焦产品全生命周期流程管理,DQA专注于设计阶段的质量控制。

a1809032425的博客 1906

深入浅出RVO、NRVO以及std::move的策略与影响

在C++编程实践中,理解返回值优化(Return Value Optimization, RVO)和命名返回值优化(Named Return Value Optimization, NRVO)对于编写高效的代码至关重要。这些编译器优化技术可以显著减少不必要的对象拷贝,优化程序的性能。同时,std::move的出现增加了对于对象资源管理的精细控制,但其滥用可能会破坏编译器的优化。本文将深入探讨RVO、NRVO以及std::move之间的关系和影响。

qq_42896106的博客 1042

3.DJI-payload SDK自定义控件的设计:

自定义控件(Widget)是指负载运行时,在用户界面中显示的自定义 UI 控件,以帮助用户快速查看负载独特的设备状态,或者对负载设进行设置。这些 UI 控件可以显示在以下用户界面中。Pilot 2 应用程序;大疆智图;基于 MSDK 开发的应用程序。这种自定义控件并不是通过 MSDK 实现的,而是通过 PSDK 的配置实现的。。

哒宰的博客 770

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C++进阶之右值引用std::move:用法实例(四百零六)

本篇目的:C++进阶之右值引用std::move:用法实例基本概念std::move 是 C++11 引入的标准库函数模板,定义于<utility>头文件中。它的作用是将一个左值强制转换为右值引用,从而触发移动语义。功能std::move 不进行任何实际的“移动”操作,只是类型转换工具。其主要用途是在对象赋值或构造时,启用移动构造函数或移动赋值运算符,避免昂贵的深拷贝。使用限制被 std::move 修饰的对象之后状态未定义,仅适用于临时资源转移场景。

Android系统攻城狮 909

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++ C11 移动语义和右值引用

对于含有堆内存的类,我们需要提供深拷贝的拷贝构造函数,如果使用默认构造函数,会导致堆内存的重复删除。,这样就避免了对临时对象的深拷贝,提高了性能。但上面的深拷贝可行,但在。默认构造函数是浅拷贝。没有深拷贝,只有浅拷贝。

waltWu0705的博客 1339

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C++核心准则-F.48 不要返回使用std:move从局部变量获得的右值引用​

F.48: Don't return std::move(local) F.48 不要返回使用std:move从局部变量获得的右值引用 Reason(原因) With guaranteed copy elision, it is now almost always a pessimization to expressly use std::move in a return statement...

面向对象思考 4278

C++返回值优化演进:从拷贝灾难到移动语义与强制省略

在C++编程中,函数返回值的传递方式直接影响程序性能。早期C++98标准下,按值返回大对象往往引发多次拷贝,成为性能瓶颈,开发者常借助输出参数或裸指针规避,却牺牲了可读性。C++11引入移动语义,通过右值引用实现资源转移,使返回开销降至常数级别。C++17进一步强制复制省略,让纯右值返回零拷贝成为标准行为。理解这些演进,有助于根据场景选择正确的返回值策略,合理应用RVO和移动语义,编写既高效又清晰的现代C++代码。

weixin_29722783的博客 225

探索 std::move

C++里通常分为左右值,但标准里更细化 这里需要明确右值的概念: 纯右值 prvalue 是没有标识符、不可以取地址的表达式,一般也称之为“临时对象”。 最常见的情况有: 返回非引用类型的表达式,如 x++、x + 1、make_shared(42) 除字符串字面量之外的字面量,如 42、true C++11 开始,C++ 语言里多了一种引用类型—右值引用。右值引用的形式是 T&&,比左值引用多一个 & 符号。 我们使用右值引用的目的是实现移动,而实现移动的意义是减少运行的开销

L-Super的博客 1701

move std 函数 示例_右值引用和std:move()

右值rvalues是怎么产生的?想必你知道,当对象没有命名时,它就是一个右值。一般来说,右值是一个临时的对象,作为函数调用的返回或者其他操作的结果,存在于栈中:从一个函数调用的返回值会转变成一个右值。也就是说,一旦你调用返回一个对象,但这个对象的命名又从来没有存在过,它就变成了右值。同样的,调用一个函数会返回一个右值。返回的值没有命名。Move构造函数与右值一起使用Move构造函数与拷贝构造函数类...

weixin_36474829的博客 556

C++返回值优化(RVO)原理与实践:消除拷贝提升性能

在C++编程中,函数返回值传递常涉及临时对象的构造与拷贝,这是影响程序性能的关键环节。编译器通过返回值优化技术,能够将函数内部构造的对象直接放置在调用方的目标内存位置,从而消除不必要的拷贝开销。这项优化不仅提升了资源管理类(如容器、字符串)的传递效率,更与现代C++的移动语义形成协同,为编写高性能代码提供了底层保障。理解RVO的工作原理、实施条件及其与std::move的交互,有助于开发者在实际工程中避免常见陷阱,编写出更利于编译器优化的代码。本文通过具体示例解析了RVO在工厂函数、STL容器等场景中的应用

weixin_34025151的博客 294

鼓励使用以const引用传参代替值传参_右值引用和std:move()

右值rvalues是怎么产生的?想必你知道,当对象没有命名时,它就是一个右值。一般来说,右值是一个临时的对象,作为函数调用的返回或者其他操作的结果,存在于栈中:从一个函数调用的返回值会转变成一个右值。也就是说,一旦你调用返回一个对象,但这个对象的命名又从来没有存在过,它就变成了右值。同样的,调用一个函数会返回一个右值。返回的值没有命名。Move构造函数与右值一起使用Move构造函数与拷贝构造函数类...

weixin_39575565的博客 1408

C++杂记——返回值优化(RVO,Return Value Optimization)、具名返回值优化 (NRVO,Named Return Value Optimization)

返回值优化(RVO):当函数返回一个对象的临时副本时,编译器可能会调用移动构造函数来优化性能,尤其是在对象较大的情况下。

czw 616

C++右值引用

-----在没有返回值优化的情况下,拷贝构造函数调用了两次,一次是GetA() 函数内部创建的对象返回后构造一个临时对象产生的。在C++中,当函数返回一个对象时,如果这个对象是通过值传递的,则会经历复制构造过程即:copy construct当GetA()调用时,它创建了一个局部对象A,调用构造函数,输出construct:1当GetA()返回时,这个局部对象的生命周期即将结束,因此需要创建一个临时对象来保存返回值,此时调用复制构造函数,输出copy construct:1。

weixin_51883798的博客 1066

C++ 返回值优化 RVO

C++ 返回值优化 RVO引子返回值优化 RVORVO 限制参考 在调试 C++ 拷贝和移动系列构造函数时候,发现构造函数调用和预期不太一样,经过查阅相关资料,发现是返回值优化(RVO)从中做梗。了解了事实真相后,今天就和大家聊聊这个 RVO。 引子 直接看一个例子: #include <iostream> class A { public: A (){ std::cout << "A(): addr= " << this << st

Dong_HFUT的博客 6525
上一篇: snprintf和strcpy和strncpy的区别
下一篇: ld脚本简述
FirMoonLight
博客等级 码龄10年 16粉丝 33原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值