右值引用是什么,把它拆开来说,首先是个引用,即一个变量的绑定或别名。然后这个变量是右值,可以理解为临时变量。
右值是什么,在赋值表达式右端通过计算产生的中间结果或字面量。比如int c =a+b, a+b即为右值,它会产生个a+b的临时变量,然后通过拷贝赋值给c, 这个临时变量随后就会被销毁,但是如果使用右值引用捕获,它就会像左值一样继续延续下来。比如int&& a = a+b。所以右值引用,是右值的引用,的表从属关系。
右值引用不能捕获左值,但对于闲置的左值,可以通过std::move转换成右值。然后被右值引用捕获。
右值引用最大的作用是语义上的,它表明自身引用的是闲置变量,因此可以把这个引用里的资源挪走用于他处,但要是显式的(比如通过移动构造函数)。而左值引用更关注的是把它修改。
移动构造函数和移动赋值函数使用右值引用捕获右值,从而达到定义和赋值变量时减少资源拷贝的目的。
代码示例
#include <cstdio>
#include <vector>
#include <iostream>
#define dbg(x) std::cout << #x ": " << x << std::endl;
std::ostream &operator<<(std::ostream &out, const std::vector<int> &v) {
bool first = true;
for (auto e : v) {
if (!first) {
out << ",";
}
out << e;
first = false;
}
out << std::endl;
return out;
}
void test_move() {
puts("test_move");
std::vector<int> v{1, 2, 3};
dbg(v); // 1 2 3
std::vector<int> w(
std::move(v)); // 将左值转换成右值, 然后再通过移动构造函数,转移资源
dbg(v); // v:
dbg(w); // w: {1 ,2, 3}
int a = 1;
int b = std::move(a); // int 类型不支持移动构造, 无法转移资源
dbg(a); // a: 1
dbg(b); // b: 1
}
void test_lref() {
puts("test_lref");
std::vector<int> &&r = std::vector<int>{1, 2, 3}; //右值引用绑定右值
dbg(r); // 1 2 3
std::vector<int> x(
r); // 命名的右值引用是左值,表明虽然引用的是闲置资源,但仍然需要显示移动资源
dbg(r); // 1 2 3
dbg(x); // 1 2 3
std::vector<int> v;
v = static_cast<std::vector<int> &&>(
x); // 未具名的右值引用为右值,比如move,或static_cast<std::std::vector<int>&&>
dbg(v); // 1 2 3
dbg(x); //
}
void test_lref_sim() {
puts("test_lref_sim");
int &&a = 1; // 右值可以看作临时变量, 右值引用延续临时变量的生命
dbg(a); // a 1
printf("address a: %p\n", &a); // address a: 0x7ffee87ab6bc
int &&b = 1;
dbg(b); // b 1
printf("address b: %p\n", &b); // address b: 0x7ffee87ab6ac
int &&c = a + 0;
dbg(c); // c 1
const int &d = 1;
printf("address d: %p\n", &d); // 常量左值引用都可绑定,只是无法修改
// int &&c = a; 左值不能直接赋值给右值引用
int x = 12;
int &&y = std::move(x); // 右值引用是个引用
y = 13;
dbg(x); // 13
dbg(y); // 13
}
int main() {
test_lref_sim();
test_lref();
test_move();
return 0;
}
网友评论