在移动构造函数和移动赋值函数之前,我们已经知道了拷贝构造函数(将对象当作参数传入,在函数体内进行浅拷贝或者深拷贝以完成数据的拷贝复制)和赋值函数(重载运算符,操作和拷贝构造函数差不多)
这里引入右值的概念,和先前左值是相对应的
右值和左值
左值:指表达式结束后依然存在的持久化对象,有名字的对象都是左值
右值:表达式结束后就不再存在的临时对象,右值没有名字
除了有无名字这个判断方法外,还有一种方法是看能否对表达式取地址,如果能,则为左值,否则为右值
右值又细分为将亡值和纯右值
将亡值:1.非引用返回的临时变量;2.运算表达式产生的结果;3.字面常量(C风格字符串除外,它是地址)
纯右值:与右值引用相关的表达式
举例: int a=7;//a为左值,7为右值
A a=cmp();//cmp()返回一个A类临时变量,a为左值,cmp()为右值
之前我们学过左值引用(为左值取别名),右值引用也是同样的道理
左值引用和右值引用
左值引用:数据类型& 变量名=左值;
右值引用:数据类型&& 变量名=右值;
举例:int &&a=8;//为右值8取别名为a,a是一个左值
右值引用使得一些将亡值重获新生,延长了其生命周期
左值引用只能绑定左值(除常量左值引用外),右值引用只能绑定右值
常量左值引用可以绑定非常量左值,常量左值和右值
如:const int&a=7;//注意常量左值引用的右值不能修改
引入右值引用的原因是为了实现移动语义
移动语义是转移对象资源的操作,拷贝语义是将对象资源进行拷贝复制的操作
移动语义
移动构造函数:类名(类名&& 源对象){......}//与拷贝构造函数相对应
移动赋值函数:类名& operator=(类名&& 源对象){……}//与赋值函数相对应
参数是右值则调用移动构造函数,是左值则调用拷贝构造函数,如果想让左值调用移动构造函数,可以使用move()方法将左值强制转换为右值,move()函数返回左值的右值形式,左值的资源所属不会发生改变
如没有移动构造函数/移动赋值函数,则会调用拷贝构造函数/赋值函数(条件满足,移动优先)
如果出现函数嵌套的情况时可能会出现错误:
void fun1(int&ii);
void fun1(int&&ii);
void fun2(int i)
{
fun1(i)
}
int main()
{
fun2(8);
}
当你想通过fun2调用fun1的右值版本时,却调用了左值版本,这就是不完美的转发
完美转发
完美转发是指函数不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变
C++11提供了以下方案:
1.如果模板中(包括类模板和函数模板)函数的参数书写成为T&& 参数名,那么,函数既可以接受左值引用,也可以接受右值引用//普通函数不可行
2.提供了模板函数std::forward<T>(参数) ,用于转发参数,如果 参数是一个右值,转发之后仍是右值引用;如果参数是一个左值,转发之后仍是左值引用
例:
template<typename TT> void func2(TT&& ii) { func1(forward<TT>(ii)); }