C++中的右值引用
摘要
本文介绍C++中右值引用的含义、以及注意事项。
右值引用的含义
《C++ primer》第5版中说明了右值引用的含义:
所谓右值引用就是必须绑定到右值的引用
举例来说:
int i = 42;
int &r = i; // 左值引用绑定到左值上
int &&rr = i; // 错误,右值引用不能绑定到左值上
int &&rri = i * 42; //正确,i*42是右值
此外,《C++ primer》中还说明了右值引用的重要性质:
只能绑定到一个将要销毁的对象
需要注意的是,右值引用变量本身是左值,所以不能用一个右值引用变量初始化一个右值引用。
int &&rx = rri; //错误,rri是一个右值引用变量,是左值
那么,到底为什么说一个右值引用变量是一个左值呢?左右值的理解就是,
- 程序员可以取地址的是左值。
- 程序员不能取地址的是右值。
这里,rri
是一个变量,具有名字,可以取地址,所以说是左值。
左值引用和右值引用的区别
学了左值引用和右值引用,自然要问这两个的区别是什么?左值引用和右值引用的区别就是初始化使用的规则不一样,其余都一样。
左值引用:只能使用左值进行初始化(除了,常量左值引用可以使用字面量初始化)
右值引用:只能使用右值初始化
在使用的时候,左值引用和右值引用无任何区别。
编译器如何对待右值引用?
既然右值引用只能绑定到右值上,并且右值引用变量本身是左值,自然就有以下疑问:
问题1:右值引用绑定的是右值,而右值是将要消亡的,但是右值引用变量本身又是左值,左值就代表着是持久的。那么把编译器是如何实现的?
问题2:按照我的这篇随笔中的理解,我把右值理解成水,把引用理解成标签,那么把一个右值引用(标签)绑定到了右值(水)上,就说不通了,怎么解释?
来看一下汇编代码:

也就是说,当一个即将消亡的值被一个右值引用变量绑定时,编译器会先把该值保存到栈上,然后把保存位置的内存地址赋值给右值引用。 从水桶和水的关系解释,编译器先创建了一个水桶,把水倒进水桶,并把标签贴到该水桶上。这就是当一个右值引用绑定到右值时,编译器的做法。
用法
当然,平时使用的话还是无需关心编译器怎么处理的细节,只需要关心什么时候能用,什么时候不能用就行了。
什么时候不能用:
- 函数返回内部变量的右值引用:
比如:
int && test() {
int x = 1;
return x + 1; // 未定义行为,最好别用
}
什么时候能用:
右值引用主要的用途:
- 实现移动语义。
- 实现完美转发。
总结
本文关键点:
- 右值引用只能绑定到右值上。
- 右值引用变量是左值。
- 右值引用变量指向的是一片内存,这片内存中存了值。所以,一个函数不能返回内部值的右值引用。
- 使用右值引用实现移动语义和完美转发。