1.什么叫左值引用,什么叫右值引用?
右值引用是C++11引入的,与之对应C++98中的引用统称为左引用。左引用的一个最大问题就是,它不能对不能取地址的量(比如字面量常量)取引用。比如int &a = 1;就不可以。
为此专门定义了左值和右值,能取地址的都是左值,反之是右值。通过右值引用,可以增长变量的生命周期,避免分配新的内存空间.
并用&&来表示右值引用,这样就可以int &&a = 1;并用&来表示左值引用。
总结:左值引用只能绑定左值;右值引用只能绑右值,但常量左值引用可以绑字面量,比如const int &b = 10;已命名的右值引用,编译器会认为是一个左值;临时对象是左值。
引申问题:i ++和++i 有什么区别?(它返回值哪个是左值,哪个是右值)
i++是左值,并且可以赋值,i++是先返回i再去+1,是右值。而++i是先加再返回,返回的是增加过的,是可以左值
2.什么是将亡值,什么是纯右值
所谓纯右值就是临时变量或者字面值,将亡值是C++11新定义的将要被“移动”的变量,比如move返回的变量。
3.移动语义与完美转发
移动语义(move semantic):某对象持有的资源或内容转移给另一个对象。为了保证移动语义, 必须记得用std::move 转化左值对象为右值,以避免调用复制构造函数.
完美转发(perfect forwarding): 为了解决引用折叠问题,必须写一个任意参数的函数模板,并转发到其他函数. 为了保证完美转发,必须使用std::forward, 我们希望左值转发之后还是左值,右值转发后还是右值.
4.什么是引用折叠?forward函数的原理
引用折叠就是,如果间接创建一个引用的引用,那么这些引用就会折叠。规则:
&& + &&->&& : 右值的右值引用是右值
&& + &->& : 右值的左值引用是左值
& + &&->& : 左值的右值引用是左值
& + &->& : 左值的左值引用是左值
为此引入了forward函数:
// 精简了标准库的代码,在细节上可能不完全正确,但是足以让我们了解转发函数 forward 的了
template<typename T>
T&& forward(T ¶m){return static_cast<T&&>(param);
}
传入 forward 实参是右值类型: 根据以上的分析,可以知道T将被推导为值类型,也就是不带有引用属性,假设为 int 。那么,将T = int 带入forward。param在forward内被强制类型转换为 int &&,还是右值引用。最终保持了实参的右值属性,转发正确
传入 forward实参是左值类型:根据以上的分析,可以知道T将被推导为左值引用类型,假设为int&。那么,将T = int& 带入forward。引用折叠一下就是 int &类型,转发正确。
5.什么是移动构造和移动赋值?
移动构造函数能直接使用临时对象已经申请的资源,它以右值引用为参数 ,拷贝以左值。
由于临时对象是右值,这里就需要使用一个move函数,它的作用的将左值强制转换为右值。
移动赋值是在赋值运算符重载的基础上,将对象右值引用作为形参进行拷贝或者赋值,从而避免创建新对象。
下面的例子展示了拷贝构造函数、赋值运算符重载、移动拷贝和移动赋值运算符重载,请仔细区别
class A{public://拷贝构造函数A(A& a) : x(a.x){cout << "Copy Constructor" << endl;}//赋值运算符A& operator=(A& a){x = a.x;cout << "Copy Assignment operator" << endl;return *this;}//移动拷贝A(A&& a) : x(a.x){cout << "Move Constructor" << endl;}//移动赋值A& operator=(A&& a){x = a.x;cout << "Move Assignment operator" << endl;return *this;}private:int x;
}