常量左值引用的特性显得更加有趣,它除了能引用左值,还能够引用右值,比如:
int &x1 = 7; // 编译错误
const int &x = 11; // 编译成功
在上面的代码中,第一行代码会编译报错,因为int&无法绑定一个int类型的右值,但是第二行代码却可以编译成功。
虽然在结果上const int &x = 11和const int x = 11是一样的,但是从语法上来说,前者是被引用了,所以语句结束后11的生命周期被延长,而后者当语句结束后右值11应该被销毁。
右值引用是一种引用右值且只能引用右值的方法。
int i = 0;
int &j = i; // 左值引用
int &&k = 11; // 右值引用
右值引用的特点之一是可以延长右值的生命周期。
# include <iostream>
class X {
public:
X() { std::cout << "X ctor" << std::endl; }
X(const X&x) { std::cout << "X copy ctor" << std::endl; }
~X() { std::cout << "X dtor" << std::endl; }
void show() { std::cout << "show X" << std::endl; }
};
X make_x()
{
X x1;
return x1;
}
int main()
{
X &&x2 = make_x();
x2.show();
}
在理解这段代码之前,让我们想一下如果将X &&x2 = make_x()这句代码替换为X x2 = make_x()会发生几次构造。在没有进行任何优化的情况下应该是3次构造,首先make_x函数中x1会默认构造一次,然后return x1会使用复制构造产生临时对象,接着X x2 =make_x()会使用复制构造将临时对象复制到x2,最后临时对象被销毁。
以上流程在使用了右值引用以后发生了微妙的变化。编译如下:
X ctor
X copy ctor
X dtor
show X
X dtor
从运行结果可以看出上面的代码只发生了两次构造。第一次是make_x函数中x1的默认构造,第二次是return x1引发的复制构造。不同的是,由于x2是一个右值引用,引用的对象是函数make_x返回的临时对象,因此该临时对象的生命周期得到延长,所以我们可以在X &&x2 = make_x()语句结束后继续调用show函数而不会发生任何问题。
延长临时对象生命周期并不是这里右值引用的最终目标,其真实目标应该是减少对象复制,提升程序性能。