最近看拷贝复制部分内容的时候看到移动构造函数和移动赋值运算符的声明中有个 &&
符号,另外在有些库里也看到了这个符号,所以把这个右值引用集中学习了一下,同时做了一些输出,希望也可以帮助到大家。
C 语言中的左/右值和 C++ 中的左/右值是不一样的,C 语言中的左值可以位于赋值语句的左侧,右值不能,比较直观,但 C++ 中的左值和右值里面的内容就比较多一些。
1. 左值和右值
在 C++ 中,左值(Lvalue)是可以被赋值的表达式,通常具有内存地址,可以被引用和修改。例如,变量、数组元素和对象成员等都是左值。
右值(Rvalue)则是临时的、无法被赋值的表达式,通常是计算结果或临时对象。右值不能被引用或修改,因为它们没有明确的内存地址。例如,常量、字面量和临时对象都是右值。
左值和右值的主要区别在于内存持久性,左值有持久的状态,比如变量,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
《C++ Primer》 中对左值和右值有个形象的描述:当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
所以当一个左值被当成右值使用时,实际使用的是它的内容(值)。在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。比如取地址符 &
,就是对一个左值取地址,取出来的地址是个右值,因为右值只有内容,在内存中没有位置。而对一个地址解引用 *p
,或者对一个数组取下标 arr[0]
,就获得了左值。有时候左值不一定可以放在表达式左边,因为有些左值不能被赋值,比如数组名和常量。
2. 左值引用和右值引用
C++11 引入了右值引用 &&
的概念,允许将右值绑定到一个引用上,并且可以修改其内容,这提供了更多的灵活性和效率。
右值引用指向将要被销毁的对象,比如一个表达式。
int i = 10;
int& j = i; // 正确:左值引用
int& k = i * 1; // 错误:左值引用不能绑定右值
int&& m = i * 1; // 正确:右值引用
int&& n = i; // 错误:右值引用不能绑定左值
const int& p = i * 1; // 正确:const左值引用可以绑定右值
如果说变量是左值,那么问题来了,右值引用的变量也是变量,这个变量是左值么,比如这里的 m
。
答案为左值,所以下面这个表达式是错误的:
int&& q = m; // 错误:右值引用不能绑定左值,即使这个左值是右值引用类型的变量
虽然直接把右值引用类型的变量绑定到变量上,但可以使用 move
来获取绑定到左值上的右值引用。
int&& q = std::move(m); // 正确:std::move可以将左值转换为右值
3. 使用场景
3.1 移动构造函数和移动赋值运算符
看《C++ Primer》这本书到的拷贝控制这一章的时候,就会经常碰到右值引用符号 &&
,它 常常用在移动构造函数和移动赋值运算符上。
移动构造函数和移动赋值运算符要求传入右值,这时需要使用 std::move
获取右值。
#include
#include
#include
#include
using namespace std;
class MyClass {
public:
std::string data;
explicit MyClass(std::string _data = "")
: data(std::move(_data)){};
MyClass(MyClass&& other) noexcept
{
cout