move语义

值类别(value category)如下:

lvalue:左值,在内存中有地址,可被程序员访问,可以放在赋值运算符左侧,也可以放在赋值运算符右侧,常见的左值有普通变量、字符串字面值“hello”等

xvalue:是个左值,但是可以被当做右值使用,需要显式的std::move将其转换为右值;

prvalue:纯右值,无地址,不可被程序员访问到,比如常数42、true、nullptr、this指针、lambda表达式对象等都是纯右值。

右值引用做形参的时候,在函数体内部被当做左值使用:

1 void func(Object&& obj)
2 {
3 // 函数体内部obj被当作左值
4
5 }

类的move构造函数的两段式写法:

 1 CObject(CObject&& other)
2 {
3 // 第一段
4 this->a = std::move(other.a);
5 this->b = std::move(other.b);
6 this->ptr = std::move(other.ptr);
7 // 第二段
8 other.ptr = nullptr;
9 }
10

类的move operator=函数的四段式写法:

 1 CObject& operator=(CObject&& other)
2 {
3 // 第一段
4 if (this == $other)
5 {
6 return *this;
7 }
8 // 第二段
9 delete this->ptr;
10 // 第三段
11 this->a = std::move(other.a);
12 this->b = std::move(other.b);
13 this->ptr = std::move(other.ptr);
14 // 第四段
15 other.ptr = nullptr;
16 }
17

编译器的返回值优化(RVO):当在栈上定义了变量并且该变量类型和返回值类型一致时,编译器会做返回值优化:

//////// case1  返回值优化RVO /////////////////////
// (1)obj在栈上申请,该类型和返回值类型一致
CObject foo()
{
return CObject();
} // (2)基于以上原因,只会调用一次构造函数
CObject object = foo(); //////// case2 具名返回值优化NRVO /////////////////////
// (1)obj在栈上申请,该类型和返回值类型一致
CObject foo()
{
CObject obj;
return obj;
} // (2)基于以上原因,只会调用一次构造函数
CObject object = foo();

注意:
(1)同一个变量只能move一次

(2)类的右值引用类新构造函数(转移构造函数)和operator=(转移赋值运算符)依赖其实现,所以在使用的时候要了解其这两个构造函数的实现细节,以免引起误用。

(3)一个变量在被std::move之后就不应该再使用,除了重新被赋值或者被析构掉。

完美转发

完美转发必须存在推理(也就是必须是模版),只有如下一种存在形式,其他形式都是右值引用而不是完美转发

引用折叠规则

  • 若一个右值引用(即带有&&)参数被一个左值或左值引用初始化,那么引用将折叠为左值引用。(即:T&& & –> T&)
  • 若一个右值引用参数被一个右值初始化,那么引用将折叠为右值引用。(即:T&& && 变成 T&&)。
  • 若一个左值引用参数被一个左值或右值初始化,那么引用不能折叠,仍为左值引用(即:T& & –>T&,T& && –>T&)。
1 template<class T>
2 void foo(T&& value) // 实参传入会引发引用折叠!
3 {
   // (0) 这里的value是个左值(具名的右值引用是左值),而实参可能是左值或右值,因此完美转发就是保留实参的实际类型。
   // (1)传入左值或左值引用时,折叠结果T为T&
// (2)传入右值或右值引用时,折叠结果T为T&&
4 bar(std::forward<T>(value));
5 }

demo

 1 #include <iostream>
2
3 class CTest
4 {
5 public:
6 CTest()
7 {
8 std::cout << "CTest::CTest" << std::endl;
9 }
10
11 ~CTest()
12 {
13 std::cout << "CTest::~CTest" << std::endl;
14 }
15
16 CTest(const CTest& t)
17 {
18 std::cout << "CTest::const CTest&" << std::endl;
19 }
20 CTest(CTest& t)
21 {
22 std::cout << "CTest::move CTest&" << std::endl;
23 }
24
25 };
26
27 void funcImpl(const CTest& t)
28 {
29 std::cout << "funcImpl left" << std::endl;
30 }
31 void funcImpl(CTest&& t)
32 {
33 std::cout << "funcImpl right" << std::endl;
34 }
35
36
37 template <typename T>
38 void func(T&& t)
39 {
      // 这里t为左值(一个具有的右值引用为左值)
40 funcImpl(std::forward<T>(t)); /// 关键的地方!!!
41 }
42
43 int main()
44 {
45 CTest t1;
46 func(t1); // t1为左值,左值与T&&的折叠结果T为左值引用T&
47
48 std::cout << std::endl << "---------------------------" << std::endl;
49 CTest t2;
50 func(std::move(t2)); // std::move(t2)为右值,右值与右值引用折叠为T&&
51
52 return 0;
53 }

输出

CTest::CTest
funcImpl left

---------------------------
CTest::CTest
funcImpl right
CTest::~CTest
CTest::~CTest

 函数变参模板右值引用

Args可以接受0或多个各种类型的参数,此时可以适配T的各种类型的构造函数。

智能指针MySharedPtr的构造函数只跟T的各种形式有关,与T本身的构造函数无关。

#ifndef __ALGO__
#define __ALGO__ #include "mysharedptr.h" template <class T, class ... Args>
MySharedPtr<T> make_shared(Args&&... args)
{
return MySharedPtr<T>(new T(std::forward<Args>(args)...));
} #endif

  

noexcept

noexcept声明的函数表示函数不会发生异常情况,在某些容器中,如果容器元素对象的move拷贝构造和move构造被声明为noexcept的,那么编译器就会优先使用move版本的拷贝构造和move构造,这样会加快插入速度(默认使用非move版本);

什么时候应该用noexcept?

(1)move拷贝构造

(2)move构造

(3)swap函数

(4)内存分配器的deallocate函数

关于C++异常在工程实践里的使用建议:

(1)尽量不使用C++异常,因为写出C++异常安全的代码是困难的

(2)小内存申请默认成功

(3)大内存申请结果判空

(4)标注库异常直接崩溃即可

move语义和完美转发的更多相关文章

  1. Effective Modern C++:05右值引用、移动语义和完美转发

    移动语义使得编译器得以使用成本较低的移动操作,来代替成本较高的复制操作:完美转发使得人们可以撰写接收任意实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数所接收到的完全相同的实参.右值引 ...

  2. [c++11]右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  3. [转][c++11]我理解的右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  4. (原创)C++11改进我们的程序之move和完美转发

    本次要讲的是右值引用相关的几个函数:std::move, std::forward和成员的emplace_back,通过这些函数我们可以避免不必要的拷贝,提高程序性能.move是将对象的状态或者所有权 ...

  5. c++11 标准库函数 std::move 和 完美转发 std::forward

    c++11 标准库函数 std::move 和 完美转发 std::forward #define _CRT_SECURE_NO_WARNINGS #include <iostream> ...

  6. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part8:Perfect Forwarding(完美转发):解决方案

    本文为第八部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  7. C++ 左值与右值 右值引用 引用折叠 => 完美转发

    左值与右值 什么是左值?什么是右值? 在C++里没有明确定义.看了几个版本,有名字的是左值,没名字的是右值.能被&取地址的是左值,不能被&取地址的是右值.而且左值与右值可以发生转换. ...

  8. C++11(列表初始化+变量类型推导+类型转换+左右值概念、引用+完美转发和万能应用+定位new+可变参数模板+emplace接口)

    列表初始化 用法 在C++98中,{}只能够对数组元素进行统一的列表初始化,但是对应自定义类型,无法使用{}进行初始化,如下所示: // 数组类型 int arr1[] = { 1,2,3,4 }; ...

  9. C++11 变长模版和完美转发实例代码

    C++11 变长模版和完美转发实例代码 #include <memory>#include <iostream>#include <vector>#include ...

  10. 第16课 右值引用(3)_std::forward与完美转发

    1. std::forward原型 template <typename T> T&& forward(typename std::remove_reference< ...

随机推荐

  1. web3.js:使用eth包

    原文在这里 简介 web3-eth包提供了一套强大的功能,可以与以太坊区块链和智能合约进行交互.在本教程中,我们将指导您如何使用web3.js版本4的web3-eth包的基础知识.我们将在整个示例中使 ...

  2. 4G EPS 中建立 eNB 与 MME 之间的 S1 连接

    目录 文章目录 目录 前文列表 S1 连接 eNB 的 S1 连接 UE 的 S1 连接 前文列表 <4G EPS 中的小区搜索> <4G EPS 中的 PLMN 选择> &l ...

  3. MegaCli64查看磁盘损坏,错误个数统计情况

    如下,两个命令,是磁盘濒临崩坏,比如存在扇区损坏之类的事情发生.咨询的浪潮热线,报sn.他们的临界值是500,我们监控脚本是200告警.Predictive Failure Count 这个的数字比M ...

  4. python ddddocr图片验证码详解

    安装 下载地址:https://pypi.tuna.tsinghua.edu.cn/simple/ddddocr/ 安装命令: pip install D:\ChromeCoreDownloads\d ...

  5. 统计学习:EM算法及其在高斯混合模型(GMM)中的应用

    1. EM算法的基本思想 我们在应用中所面对的数据有时是缺损的/观测不完全的[1][2].我们将数据分为: 可观测数据,用\(Y\)表示: 缺失数据,用\(Z\)表示; 完全数据,用\(X=(Y, Z ...

  6. linux下使用chattr创建一个连root都无法删除的文件

    一.关于chattr,lsattr 1.chattr 的作用:改变一个Linux文件系统上的文件属性. 2.chattr用来改变文件.目录的属性,lsattr用来查看文件.目录的属性. 3.chatt ...

  7. 记一次Idea无法打开记录(idea升级)

    记一次Idea无法打开记录 前言,本来今天是打算升级Idea,然后体验一波的,结果升级完之后,发现无法打开idea(双击之后并没有任何打开的反应). 原因排查,打开idea所在目录,找到idea.ba ...

  8. k8s中的pod更新策略

    StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序,比如在进行SpringCloud项目容器化时,Eureka的部署是比较适合用StatefulSet部署方式的 ...

  9. NOIP模拟49

    虚伪的眼泪,会伤害别人,虚伪的笑容,会伤害自己. 前言 暑假集训过后的第一次考试,成绩一般,没啥好说的 T1 Reverse 解题思路 看到这个题的第一眼就感觉是最短路,毕竟题目的样子就好像之前做过的 ...

  10. LiteOS基础学习

    1 IDE环境安装 目的:安装LiteOS IDE,并且是使用仿真方式运行. 1.1 IDE安装 HUAWEI LiteOS Studio安装 (gitee.io) 1.2 中文安装 HUAWEI L ...