右值引用标记为T&&,在介绍右值引用前先了解什么是左值和右值。
左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时不再存在的临时对象。区分左右值,看能不能对表达式取地址,能取,是左值,不能取,是右值。所有具名变量或对象都是左值,而右值不具名。
c++11中右值两个概念,一个将亡值,另一个纯右值。非引用返回的临时变量、表达式产生的临时对象、原始字面量和lambda表达式等都是纯右值。将亡值是与右值引用相关的表达式,如将要被移动的对象、T&&函数返回值、std::move返回值和转换为T&&类型的转换函数的返回值。
T&&表示是一个未定的引用类型,如果&&被一个左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值。需要注意的是,只有发生自动类型推断时(如函数模板的类型自动推导,或auto关键字),&&才是一个未定的引用类型。
template<typename T>
void f(T&& param); //T需要类型推断,&&是一个未定的引用类型
template<typename T>
class Test{
Test(Test&& other); //已经定义了一个特定的类型,没有类型推断,&&是一个右值引用
};
template<typename T>
void f(std::vector<T>&& param); //&&是一个右值引用,在调用这个函数前,vector<T>中的推断类型已经确定了
template<typename T>
void f(const T&& param); //&&是一个右值引用,未定的引用类型只在T&&下发生,任何一点附加条件都会使之失效
T&&未定的引用类型存在类型推导,相比右值引用(&&)会发生类型的变化,这种变化称为引用折叠。引用折叠的规则:
(1)所有的右值引用叠加到右值引用上仍然是一个右值引用。
(2)所有其它引用类型之间的叠加都将变成左值引用。
编译器会将已命名的右值引用视为左值,而将未命名的右值引用视为右值。
void PrintValue(int& i)
{
cout << "lvalue:" << i << endl;
}
void PrintValue(int&& i)
{
cout << "rvalue:" << i << endl;
}
void Forward(int&& i) //i右值命名对象
{
PrintValue(i);
}
int i = 0;
PrintValue(i); //lvalue:0
PrintValue(1); //rvalue:1
Forward(2); //lvalue:2