1.右值引用和移动语义
左值和右值的重点区分是能否取地址。
能取地址的是左值(可以是值,也可以是表达式),不能取地址的是右值。
1.1 什么是左值
1.2 什么是右值
1.2.1 常见的右值
常见右值:常数(10),匿名对象,函数返回值(函数返回出去的是个临时对象),x+y这个表达式的结果也是右值。
但是像这种返回的就是个左值,因为它返回的是个左值引用。
补充:匿名对象是右值,有名的是左值。
1.3 右值引用
左值引用就是给左值取别名,右值引用就是给右值取别名。
上图为右值引用。
还可以通过强转的方式让左值引用 引用一个右值
1.4 右值引用的意义
引用的意义:减少拷贝,提高效率。
1.4.1左值引用的场景
左值引用的场景:一个是传引用传参,一个是传引用返回。它们都可以减少拷贝,提高效率。
但是左值引用返回值的问题没有彻底解决:如果返回值是func2中的局部对象,不能用引用返回(因为出了作用域,局部对象要销毁)!
1.4.2移动构造和移动赋值
为了解决这个问题,C++11在构造,拷贝构造的基础上重载出了移动构造:
编译器会自动匹配最合适的一个去调用。
理解移动构造:C++把右值细分为纯右值(内置类型)和将亡值(自定义类型)。而根据上文中介绍的内容我们发现,将亡值的生命周期其实并不长(一般就是其所在的那一行),为了减少拷贝,移动构造其实就是“夺舍”的做法,把将亡值中的数据换给我,把我的数据换给将亡值。这样进行简单的数据交换的效率要比深拷贝的效率高很多很多!这么做几乎可以把拷贝的效率拉到极致,这也是为何C++十分高效的重要原因!(移动赋值也是利用这个原理)。
移动构造和移动赋值提高了传值返回的效率:
图中的str虽然为左值,但是有了移动构造之后,编译器会把它们识别成右值,从而调用移动构造。
有了右值引用重载出来的移动构造,在自定义类型传参时,那些将亡值会走移动构造,将它们的资源转移过去,避免走深拷贝,从而将效率拉到极致。
同样的,有了移动构造和移动赋值之后,编译器会自动把str识别成右值,先调用移动构造,然后再调用移动赋值。
举例:验证移动构造
move可以将左值转化成右值,然后调用移动构造,将str中的资源转移到str1中。
1.4.3补充知识
1.
右值引用本身是左值(s1是左值)。只有右值引用是左值,才能实现资源的交换(移动构造,移动赋值)。
2.
3.
上文所讲的是针对自定义类型中深拷贝的类,因为深拷贝的类才有转移资源的说法,才有效率上的提升。
对于内置类型和浅拷贝的类没有转移资源的说法,效率也不会有提升。
1.5 万能引用
图中的这个模板是万能引用,它可以推导出右边的四个函数。
但是有一个问题:进入函数体中,t全是左值,只能调用Fun的左值引用,不可能调用右值引用。
为了解决这个问题,C++增加了一个东西叫完美转发:
也就是说传左值就不变,传右值会把t转化成右值。