C++11新特性之右值引用
Rvalue references (右值引用)
左值:可以出现在operator=左边的 (左值与右值的根本区别在于能否获取内存地址,能取地址的即为左值,不能取地址的即为右值)
右值:只能出现在operator=右边的 通常临时对象(将亡值)、字面值常量(纯右值)是右值
类的临时对象是一个右值,临时变量一定被当成右值,因为临时对象创建之后不会再被使用,所以直接把右值数据引用给别的变量,有时候一个左值在后面不会被用到,那么就可以使用move语义把左值转成右值。
右值引用和左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。
右值引用(&&)只能接受右值。
右值引用两大作用:移动语义和完美转发
-
移动语义
移动语义简单来说就是避免不必要的拷贝,转移资源的控制权。比如创建一个临时对象,传递给某个参数。这里是有一次拷贝的,临时对象拷贝到函数里的对象,临时对象就被销毁了。如果临时对象里有一些动态资源,就必须要顺便拷贝一份,但其实这些拷贝都是无意义的,因为临时对象拷贝完就不用了,所以不
在这里插入代码片
如把临时对象的资源拿出来直接用,即转移资源。所以函数如果聪明一点,每当传递临时对象时就不拷贝而是直接使用,传递其他对象时再进行拷贝(因为这些对象肯能在其他地方被使用,所以不能随意更改)。右值引用就是让函数知道是不是临时对象的标记。除此之外,对于某个左值,如果这个左值在以后不会被用到,那么在向函数中传参时,可以通过std::move()将该左值转换成右值,标记成临时对象,从而实现转移资源而不是拷贝资源。典型代表:unique_ptr
实现移动语义要增加两个函数:移动构造函数和移动赋值运算符。(编译器也会自动提供默认版本)
class_name ( const class_name & ); // 拷贝构造 左值引用 class_name & class_name :: operator= ( const class_name & ) //拷贝赋值 class_name ( class_name && ); // 移动构造函数 右值引用 class_name & class_name :: operator= ( class_name && ) //移动赋值
a为左值 a为右值 T b = a,c(a), d; 调用T的拷贝构造函数 调用T的移动构造函数 d = a; 调用T的拷贝赋值 调用T的移动赋值 -
完美转发 依靠std::forward()
forward()会保留参数的左右值类型。
函数func 接收一个右值为参数, 在该函数内部调用了另一个函数 foo. 我们希望当func的参数是右值的时候, foo的参数也是右值; 当 func 的参数是左值的时候, foo 的参数也是左值.
而在下面的例子, 当func 调用foo 时参数 a 又从右值转换为左值, 此时会调用 a 的拷贝构造函数.
void foo(A a) { // ... } void func(A&& a) { // ... foo(a); } //修改后 //通过forward实现完美转发 void func(A&& a) { // ... foo(std::forward<A>(a)); // 将参数 a 依照类型 A 进行转发 }