C++右值引用

一、左值、右值

1.1 什么是左值、右值

1
int a = 5;

简单来说,左值就是等号左边的值,可以取地址,也就是这里的 a;右值就是等号右边的值,不能够取地址,也就是这里的 5

  • a 可以通过 & 取地址,位于等号左边,所以 a 是左值
  • 5 位于等号右边,5 没法通过 & 取地址,所以 5 是个右值

再看一个例子:

1
2
3
4
5
6
7
8
9
struct A {
A(int a = 0) {
a_ = a;
}

int a_;
};

A a = A();
  • 同样的,a 可以通过 & 取地址,位于等号左边,所以 a 是左值
  • A() 是个临时值,没法通过 & 取地址,位于等号右边,所以 A() 是个右值

1.2 什么是左值引用、右值引用

引用本质是别名,可以通过引用修改变量的值,传参时传引用可以避免拷贝,其实现原理和指针类似。

1.2.1 左值引用

左值引用能指向左值,不能指向右值的就是左值引用:

1
2
3
int a = 5;
int &ref_a = a; // 左值引用指向左值,编译通过
int &ref_a = 5; // 左值引用指向了右值,会编译失败

引用是变量的别名,由于右值没有地址,没法被修改,所以左值引用无法指向右值。

但是,const左值引用是可以指向右值的:

1
const int &ref_a = 5;  // 编译通过

const左值引用不会修改指向值,因此可以指向右值,这也是为什么要使用const &作为函数参数的原因之一,如std::vectorpush_back

1
void push_back (const value_type& val);

如果没有constvec.push_back(5)这样的代码就无法编译通过了。

1.2.2 右值引用

再看下右值引用,右值引用的标志是 &&,顾名思义,右值引用专门为右值而生,可以指向右值,不能指向左值

1
2
3
4
5
6
int &&ref_a_right = 5; // ok

int a = 5;
int &&ref_a_left = a; // 编译不过,右值引用不可以指向左值

ref_a_right = 6; // 右值引用的用途:可以修改右值

1.2.3 让右值引用指向左值

通过 std::move 可以使右值引用指向左值:

1
2
3
4
5
int a = 5; // a是个左值
int &ref_a_left = a; // 左值引用指向左值
int &&ref_a_right = std::move(a); // 通过std::move将左值转化为右值,可以被右值引用指向

cout << a; // output: 5

注意,std::move 的唯一作用是将左值强制转化为右值,源码如下:

1
2
3
4
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

可见, 单纯的 std::move(xxx) 不会有性能提升。

同样的,右值引用能指向右值,本质上也是把右值提升为一个左值,并定义一个右值引用通过 std::move 指向该左值:

1
2
3
4
5
6
7
8
int &&ref_a = 5;
ref_a = 6;

// 等同于以下代码:

int temp = 5;
int &&ref_a = std::move(temp);
ref_a = 6;

1.3 左值引用、右值引用本身是左值还是右值

被声明出来的左、右值引用都是左值。 因为被声明出的左右值引用是有地址的,也位于等号左边。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 形参是个右值引用
void change(int&& right_value) {
right_value = 8;
}

int main() {
int a = 5; // a是个左值
int &ref_a_left = a; // ref_a_left是个左值引用
int &&ref_a_right = std::move(a); // ref_a_right是个右值引用

change(a); // 编译不过,a是左值,change参数要求右值
change(ref_a_left); // 编译不过,左值引用ref_a_left本身也是个左值
change(ref_a_right); // 编译不过,右值引用ref_a_right本身也是个左值

change(std::move(a)); // 编译通过
change(std::move(ref_a_right)); // 编译通过
change(std::move(ref_a_left)); // 编译通过

change(5); // 直接接右值,编译通过

cout << &a << ' ';
cout << &ref_a_left << ' ';
cout << &ref_a_right;
// 打印这三个左值的地址,都是一样的
}
  1. 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝
  2. 右值引用可以直接指向右值,也可以通过 std::move 指向左值;而左值引用只能指向左值(const 左值引用也能指向右值)
  3. 作为函数形参时,右值引用更灵活。虽然 const 左值引用也可以做到左右值都接受,但它无法修改,有一定局限性
1
2
3
4
5
6
7
8
9
10
11
12
void f(const int& n) {
n += 1; // 编译失败,const左值引用不能修改指向变量
}

void f2(int && n) {
n += 1; // ok
}

int main() {
f(5);
f2(5);
}

二、右值引用的应用

2.1 实现移动语义

C++11新特性(一)

2.2 完美转发

C++模板参数推导


C++右值引用
http://example.com/2026/05/10/C++右值引用/
作者
Yu xin
发布于
2026年5月10日
许可协议