【C++11右值引用】
1、什么是左值?什么是右值?
左值是表达式结束后依然存在的对象;右值是表达式结束时就不再存在的对象。
2、在早期的C++中,普通 & 无法对右值取引用。只有 const & 可以对右值取引用。
const int& i = 10;
右值引用能够解决两个问题。
1)非必要的拷贝操作
2)模板函数,按照实际类型进行转发
3、第一大特点:通过右值引用的声明,右值又“重获新生”
#include <iostream> using namespace std; int g_constructCount=0; int g_copyConstructCount=0; int g_destructCount=0; struct A { A(){ cout<<"construct: "<<++g_constructCount<<endl; } A(const A& a) { cout<<"copy construct: "<<++g_copyConstructCount <<endl; } ~A() { cout<<"destruct: "<<++g_destructCount<<endl; } }; A GetA() { return A(); } int main() { A a = GetA(); return 0; }
上面代码,会产生如下输出 :
construct: 1 copy construct: 1 destruct: 1 copy construct: 2 destruct: 2 destruct: 3
如果将 A a = GetA(); 改为下在这样,会产生编译错误。
A& a = GetA()
改为 A&& a = GetA() 是可以的。会产生如下输出 :
construct: 1 copy construct: 1 destruct: 1 destruct: 2
4、第二大特点,右值引用本身可能是左传,也可能是右值。
template<typename T> void f(T&& t){} f(10); //t是右值 int x = 10; f(x); //t是左值
5、第三大特点,universal references. T&& t在发生自动类型推断的时候,如果被一个左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。
template<typename T> void f(T&& param); template<typename T> class Test { Test(Test&& rhs); };
6、为了避免 copy constructor 中内存很大的问题,C++11中新增了 move constructor.
copy constructor 中做深拷贝。 move constructor 中做浅拷贝,并且将参数中的数据reset。
class A { public: A() :m_ptr(new int(0)){} A(const A& a):m_ptr(new int(*a.m_ptr)) //深拷贝的拷贝构造函数 { cout << "copy construct" << endl; } A(A&& a) :m_ptr(a.m_ptr) { a.m_ptr = nullptr; cout << "move construct" << endl; } ~A(){ delete m_ptr;} private: int* m_ptr; }; int main(){ A a = Get(false); }
construct
move construct
move construct
7、std::move的作用是什么?
std::move用于把任意类型转化为右值引用,以方便调用move构造函数/move赋值函数。
8、std::forward的作用是什么?把左值引用还原为左值,把右值引用还原为右值,即把尽可能把值变变右值。
template <typename T> void forwardValue(T& val) { processValue(val); //右值参数会变成左值 } template <typename T> void forwardValue(const T& val) { processValue(val); //参数都变成常量左值引用了 }
如果调用 fworwardValue(0),则 processValue(val)时,参数变成了左值引用。
forward<T>的目的是保护引用类型。
3、四大类型
1)non-const lvalue
2)const lvalue
3)non-const rvalue
4)const rvalue
4、为什么non-const lvalue只能绑定到本类对象?
首先因为const的限制non-const只能绑定到non-const,因此排除(2)、(4),再者如果绑定到non-const rvalue,则有可能导致在后续修改rvalue,这显然有巨大风险。
5、为什么non-const rvalue只能绑定到本类对象?
如果允许绑定到non-const lvalue,则有可能导致数据被窃取;如果绑定到const rvalue,const lvalue则有可能修改原数据。
6、template<typename T >
void f(T&& t){ g(t); }
我们规定:
如果实参类型为右值,那么T&&就被推导为右值引用。
如果实参类型为左值,那么T&&就被推导为左值引用。
7、有了右值引用后,需要添加以下类成员函数:
1)右值引用构造函数。
2)右值引用拷贝构造函数。
参考: