1. 什么是右值引用
右值引用是c 11新加的一种引用类型,是一种仅能绑定到右值上的引用。不同于左值引用仅用一个&表示,右值引用用两个&&表示。
int x{ 5 };
int& lref{ x }; // l-value refrence initialized with l-value x
int&& rref{ 5 } // r-value refrence initialized with r-value 5
右值引用有两个非常有用的性质: 1. 右值引用将初始化他们的对象的寿命延长到右值引用的寿命;2. 非常量的右值引用允许修改右值。
看一个实际例子:
#include
class fraction
{
private:
int m_numerator;
int m_denominator;
public:
fraction(int numerator = 0, int denominator = 1) :
m_numerator{ numerator }, m_denominator{ denominator }
{
}
friend std::ostream& operator<<(std::ostream& out, const fraction &f1)
{
out << f1.m_numerator << '/' << f1.m_denominator;
return out;
}
};
int main()
{
auto &&rref{ fraction{ 3, 5 } }; // r-value reference to temporary fraction
// f1 of operator<< binds to the temporary, no copies are created.
std::cout << rref << '\n';
return 0;
} // rref (and the temporary fraction) goes out of scope here
这段代码打印出:
3/5
fraction{ 3, 5 }是一个匿名对象(临时对象),在这行语句结束的时候就出了作用域,本来应该被销毁掉,但是我们用了一个右值引用来绑定它,因此延长了它的生命期,直到main函数结束,局部变量rref被销毁的时候,这个临时对象才会被销毁。
再看另外一个例子:
#include
int main()
{
int &&ref{ 5 };
rref = 10;
std::cout << rref << std::endl;
return 0;
}
这段代码执行结果是:
10
这里用字面值初始化一个右值引用,会创建一个临时对象,我们可以通过右值引用来修改这个对象。
2. 右值引用作为函数参数
右值引用最有用的地方在于作为函数参数,尤其是在写重载函数时,希望对传入的左值和右值表现出不同的行为。
void fun(const int& lref)
{
std::cout << "l-value reference to const." << std::endl;
}
void fun(int &&rref)
{
std::cout << "r-value reference." << std::endl;
}
int main()
{
int x{ 5 };
fun(x); // l-value argument calls l-value version of fun()
fun(5); // r-value argument calls r-value version of fun()
return 0;
}
这段代码打印出:
l-value reference to const.
r-value reference.
可以看出,当传入的参数是左值时,调用的是左值版本的fun(), 当传入的参数是右值时,调用的是右值版本的fun()。可是这有什么用呢?这对于移动语义来说是一个非常重要的特性,后面会继续讨论。
再看一个有意思的例子:
int &&ref{ 5 };
fun(ref);
ref是一个右值引用,那么fun(ref)调用的是右值引用版本吗?事实上,这里调用的是左值版本的fun()函数。虽然ref是一个右值引用,但是这仅说明它绑定的对象是一个右值,它本身是一个局部变量,是一个左值,因此这行代码调用的是左值版本的fun()。
3. 不要返回右值引用
在绝大多数情况下,你都不应该返回右值引用。因为右值引用绑定的对象在出作用域之后就会被销毁,因此从函数返回,你智能得到一个"hanging reference"。