C++11引用LValue/RValueReference
引用Reference
引用的声明:
T &ref = object ;
T & ref ( object ) ;
T && ref = object ;
T && ref ( object ) ;
引用声明时必须赋值
引用的有点类似于这种const类型的指针T *const t;
引用和指针一样,不会触发对象的构造函数和析构函数。
左值和右值LValue/RValue
按照字面意思理解:左值就是在赋值等于号左边的值,右值就是在右边的值。
左值和右值最好区别的是,左值是有变量名的,右值是没有变量名的,这些就是右值:常量“abcd”字符串,函数返回的临时对象。
左引用、右引用
左值引用声明:T & ref1;
右值引用声明:T && ref2;
引用本身是一个左值,因为它有名字,使用时ref1和ref2是相同的,当成普通变量使用。左值引用声明和右值引用声明后的变量都是一个引用,两者的区别在于,左值引用需要一个左值来赋值,指向左值;右值引用需要一个右值来赋值,指向右值。
例如:
int a;
int &r11 = a;
const int &r12 = 3;//const左值引用可以指向右值
int &&r2 =a;//编译错误,无法指向左值
int &&r3 = 3;//
int f (int &&a);
f(r3);//编译错误,r3本身是左值
左值转右值
左值可以通过std::move转成右值,例如:
int &&r2=std::move(a);//std::move(a)会把 a 当成右值,这个语句于: int &r2 =a完全等效。
class A;
A a;
A b = std::move(a); //会调用b的移动构造函数
移动构造函数
移动构造函数是使用右值引用类型当作参数的构造函数,如:
class A
{
public:
int *p;
A() :p(nullptr) {};
A(int a)
{
p = new int(a);
}
A(const A& a) //复制构造函数
{
this->p = new int(*a.p);
}
A(A &&a) //移动构造函数
{
this->p = a.p;//因为a是一个右值,函数结束后a会被释放,
//所以可以直接拿使用a的内存,无需重新用new 分配。
a.p = nullptr;
}
~A()
{
if (nullptr != this->p)
{
delete p;
this->p = nullptr;
}
}
};
因为右值一般都是临时变量,所以移动构造函数中无需再分配内存空间,可以直接使用右值中的内存,这样可以减少内存的分配次数和赋值次数。
任何函数都可以采用右值引用作为参数,例如:
void Fun (A &&a)
{
}
模板中的引用参数
VS2015 中std::make_pair函数,只有以下一个定义:
template<class_Ty1, class_Ty2>inline
_CONST_FUNpair<typename_Unrefwrap<_Ty1>::type,
typename_Unrefwrap<_Ty2>::type>
make_pair(_Ty1 &&_Val1,_Ty2 &&_Val2)
{ // return pair composed fromarguments
typedefpair<typename_Unrefwrap<_Ty1>::type,
typename_Unrefwrap<_Ty2>::type>_Mypair;
return(_Mypair(_STDforward<_Ty1>(_Val1),
_STDforward<_Ty2>(_Val2)));
}
参数部分
make_pair(_Ty1 &&_Val1, _Ty2 &&_Val2)
似乎参数只可以使用右值进行赋值。但这个语句是可以编译通过的:
int a;
std::make_pair(a, 2);
VS2015 识别的类型
是如何推导到_Ty1&& _Val1为左值呢?
引用的typedef
typedef int& lref;
typedef int&& rref;
int n;
lref& r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref& r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
除非类型为rref且用&&定义,才是右值引用类型。当lref用&&定义是还是左值引用。
所以上个例子中,_Ty1的类型为int&,所以make_pair的第一个值为左值