1.识别方法:可对表达式用“&”取值,为左值;否则,为右值。左值可以看作是“对象”,右值可以看作是“值”。
- 左值和右值的区分
int iVar;
iVar = 5;
//(iVar + 3) = 7;
解析:iVar是临时变量,&iVar可获取到地址,在寄存器上有确定的存储地址,直到离开作用域时,才销毁!
&(iVar + 3)获取不到地址,原因是iVar+3在寄存器上也是一个临时值,计算完”iVar + 3 = 7“后便失效,被销毁!
- 无法将左值引用绑定到右值上,const除外
int iData = 1;
//int& iData = 4;/*不合法,4为右值*/
int& iData1 = iData;/*合法,iData为左值*/
const int& iData2 = 6;/*合法*/
int&& iTemp = 5;/*给右值5具名*/
2.引用折叠
- 引用规则:右值引用叠加到右值引用依然是右值引用,而其他类型叠加则变为左值引用,且左值或右值独立于它的类型
int&& iTest1 = 2;/*给右值具名*/
//int&& iTest2 = iTest1;/*不可重复具名*/
int&& iTest3 = std::move(iTest1);/*转移具名,2的具名为iTest3*/
iTest1 = 5;/*右值引用可修改*/
int iTest4 = iTest1;/*正常的左值赋值*/
3.类型推导
- T&&并不一定代表右值,也可能是左值,取决于初始化值的类型.(模板参数举例)
4.作用:优化模板类的入参类型;优化性能,避免深拷贝
- 模板参数:即可以是左值,也可以是右值。
... ...
template<class T> void Function(T&& x) { auto typeData = x; };
int main()
{
Function(5);/*右值*/
int iData = 4;
Function(iData);/*左值*/
}
- 深拷贝:即解决了指针悬挂,又避免了深拷贝,特别是一些内存开销大的对象,此法就节省了这一部分的开销。
class CTest
{
private:
int* m_piPtr;
public:
CTest() : m_piPtr(new int[2]) {}
CTest(CTest&& objTest) : m_piPtr(new int[2]){}/*避免指针悬挂*/
~CTest() { if (nullptr != m_piPtr) delete m_piPtr; m_piPtr = nullptr; }
};
... ...
CTest objTest;
CTest objTest1(std::move(objTest));/*避免深拷贝*/