我们先来看一段例子,一个简单的函数返回值场景

#include <iostream>
using namespace std;
class Moveable {
public:Moveable():h(new int(3)) {cout << "construct " << endl;}   ~Moveable() {delete h;}   Moveable(const Moveable& m): h(new int(*m.h)) {cout << "copy constructor" << endl;}   /* 转移构造Moveable(Moveable&& m): h(m.h) {cout << "move constructor" << endl;m.h = nullptr;}   */int* h;
};Moveable GetTemp() {Moveable tmp;return tmp;
}int main(int argc, char** argv) {Moveable a = GetTemp();return 0;
}

如果正常的编绎,我这边使用的是clang-700.1.81,

编绎命令:g++ -std=c++11 demo_3.cpp -o demo_3

执行结果如下:

即是只有一个构造函数被调用。

场景1分析:

这个情况下,没有临时变量产生?没有拷贝构造被调用,直接就一个构造函数被调用了。

为啥?这是编绎的优化起作用了,叫作RVO技术,可以自行百度一下 。

场景2:

如何才能看到拷贝构造被调用呢?即是产生临时变量,这才是正常调用的一个过程。

编绎命令:g++ -std=c++11 -fno-elide-constructors demo_3.cpp -o demo_3

执行结果:

分析:我们看到,-fno-elide-constructors这个选项关闭了RVO优化,直接出现了两次拷贝构造的调用。第一次是return变量tmp传给临时变量。第二次是临时变量转值给变量a

场景3:

我们把转移构造函数的注释打开。代码如下:

#include <iostream>
using namespace std;
class Moveable {
public:Moveable():h(new int(3)) {cout << "construct " << endl;}   ~Moveable() {delete h;}   Moveable(const Moveable& m): h(new int(*m.h)) {cout << "copy constructor" << endl;}   Moveable(Moveable&& m): h(m.h) {cout << "move constructor" << endl;m.h = nullptr;}   int* h;
};Moveable GetTemp() {Moveable tmp;return tmp;
}int main(int argc, char** argv) {Moveable a = GetTemp();return 0;
}

编绎命令:g++ -std=c++11 -fno-elide-constructors demo_3.cpp -o demo_3

关闭RVO优化,执行结果:

这里是调用了两次转移构造。我们知道,第二次转移构造的时候,是从临时变量转移到变量a中,因为临时变量是个右值引用,触发的是转移构造,这个可以理解。问题是:第一个return tmp的时候,为什么是转移构造?tmp不是一个有命变量吗?怎么不是使用拷贝构造函数?猜想是不是编绎器做的优化?不要急,我们看看下面这个场景。

场景4:

看看代码如下:

#include <iostream>
using namespace std;class Moveable {
public:Moveable() {cout << "constructor" << endl;}   ~Moveable() {}   Moveable(const Moveable& m) {cout << "copy constructor" << endl;}   Moveable(Moveable&& m) {cout << "move constructor" << endl;}
};Moveable GetTemp(bool which) {Moveable tmp1, tmp2;return which ? tmp1 : tmp2;
}int main(int argc, char** argv) {Moveable a = GetTemp(true);return 0;
}

在GetTemp要返回处,加一个多目运算做逻辑判断。执行结果如下:

分析:这次,return tmp到函数外的时候,就真实的执行了拷贝构造,而不会被编绎器优化。

所以场景3中的,return tmp被当成右值,其实是编绎器进行的优化。

场景4中,如果还想让返回出去的临时变量赋值调用转移构造,可以使用std::move()去强制转换。

即是:return which ? tmp1 : tmp2; 修改为

return std::move(which ? tmp1 : tmp2),即是不会调用拷贝构造函数。

别外,有个stackoverflow讲述了这个问题:

https://stackoverflow.com/questions/14856344/when-should-stdmove-be-used-on-a-function-return-value

总结:这里简单总结了,RVO与函数返回值转换成右值引用的问题。希望后续对这些语言特征有更深入理解。

函数返回值的优化技术(RVO和右值引用)相关推荐

  1. c++中返回值优化(RVO)和命名返回值优化(NRVO)介绍

    RVO和NRVO介绍 前言 半年前就想写一篇关于RVO和NRVO的介绍,但碍于没什么时间去写博客.在跟身边人进行学术探讨的时候,会发现部分人可能尝到了编译器给它做返回值优化的好处,知道这段代码被优化了 ...

  2. c++函数返回值是一个引用

    函数返回值是一个引用的情况: 1.值是如何被返回的: 返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果. 如果函数返回引用,则该引用仅是它所引对象的一个别名. 2.不能返回局部对象的引 ...

  3. C++函数返回引用和值问题

    今天因为改到一个代码,所以遇到一个问题.函数里局部变量返回的问题. 以下是代码,就是运用类模板,重载了+操作: 一.最原始的代码如下: <pre name="code" cl ...

  4. C++基础知识(二)--左值右值--逻辑表达式求值优化--逗号运算符与表示式--输入输出格式控制...

    :一.C++左值右值概念 左值:c++将变量名代表的单元称为左值,而将变量的值称为右值,左值必须是内存中可以访问且可以合法修改的对象,因此只能是变量名,而不能是常量或表达式.即左值可以寻址. 右值:将 ...

  5. 面试准备每日五题:C++(七)——左值右值、面向对象、四种cast转换、拷贝构造函数赋值、虚函数多态

    文章目录 一. 什么是右值引用,跟左值又有什么区别? 二. 面向对象的三大特征 三. c++中四种cast转换 四.拷贝构造函数和赋值运算符的认识 五. 对虚函数和多态的理解 一. 什么是右值引用,跟 ...

  6. 也来谈谈函数返回引用 int fun(int x);

    我们先来看一个基本的东西, 对应一个整形的a, 有两种理解:1. a表示a这个容纳箱, 2. a表示a这个容纳箱中的值.  其实前者就是左值, 后者是右值. 大家应该是通过成个程序来认识引用的: #i ...

  7. C++ 对象移动(右值引用()、移动构造函数、移动赋值运算符、引用限定函数)

    原文:对象移动(右值引用(&&).移动构造函数.移动赋值运算符.引用限定函数) 一.对象移动概述 C++11标准引入了"对象移动"的概念 对象移动的特性是:可以移动 ...

  8. C++11标准之右值引用(ravalue reference)

    C++11标准之右值引用(ravalue reference) 1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具 ...

  9. 看完这个你还不理解右值引用和移动构造 你就可以来咬我(上)

    共分三篇,这是第一篇.另外两篇,看完这个你还不理解右值引用和移动构造 你就可以来咬我(中),看完这个你还不理解右值引用和移动构造 你就可以来咬我(下). C++ 右值引用 & 新特性 C++ ...

最新文章

  1. YOLO-FastestV2:更快,更轻!移动端高达300 FPS!参数量仅250k
  2. SCI写作常用句式总结,帮你迅速提升paper档次
  3. 适合于小团队产品迭代的APP测试流程
  4. 做移动端视频通话软件,大致看了下现有的开源软件(转)
  5. cv_bridge 调用ros自带的opencv版本的解决
  6. Java Servlet(八):EL自定义函数
  7. ORACLE常用的一些特殊SQL,收藏收藏,下次需要的时候就不用再翻箱倒柜了
  8. 极简主义︱利用apple机器学习平台Turicreate实现图像相似性检索(二)
  9. OObjective-c UIView 蒙层
  10. 为什么谐振时电抗为0_变频谐振耐压试验装置在进行电缆耐压试验原理
  11. firebase_crashlytics缺失dSYM unity ios
  12. Struts中拦截器和过滤器的区别
  13. 电子秤查看通道及更改通道方法
  14. 进度图绘制十大注意事项
  15. matlab定积分例子,利用Matlab进行不定积分运算示例巧妙至极.doc
  16. json代码恶搞地图完整版qqxml地图卡片代码
  17. 三重积分--------球坐标系
  18. 流利阅读 2019.2.2 Barbie will soon be 60—and is still going strong
  19. storyboard(故事版)新手教程 图文详解 3.在故事版上使用scrollview
  20. 21-7-09 主要元素

热门文章

  1. 三星 linux手机系统版本,WindowsMobile操作系统手机版本分类对应机型
  2. 刘振飞BugFree管理系统的功能与使用(一)
  3. 初来北漂,黑中介“诈”道,58、赶集租房网缺
  4. Android:销毁所有的Activity退出应用程序几种方式
  5. Jest测试框架入门之匹配器与测试异步代码
  6. P3239 [HNOI2015]亚瑟王(巧妙的概率dp)
  7. 语录集人生---投资
  8. python中true是什么意思_Python解惑之True和False详解
  9. ESP32双核CPU,利用核0实现蓝牙打印机打印,核1完成常规控制
  10. 复现 MonoEF:Monocular 3D Object Detection: An Extrinsic Parameter Free Approach