C++ 中的右值引用

Jul 08, 2022 • 预计阅读时间 2 分钟

C++ 里的表达式有左值和右值的概念。

一个表达式(expression)要么是广泛左值类型(glvalue),要么是右值类型(rvalue)。

广泛左值(glvalue),还区分为左值(lvalue)和将亡值(xvalue)。

右值类型(rvalue),也区分为纯右值(prvalue)和将亡值(xvalue)。

左值引用符:&,右值引用符:&&

左值

可以引用到其地址的都是左值类型。

虽然字面量属于纯右值,但有唯一例外:字符串字面量是左值类型。

右值

不能通过地址对其进行访问的属于纯右值,比如字面量、函数返回非引用类型的值、由表达式生成的临时变量:

int a = 5; // 5 是字面量,是纯右值。变量 a 是左值

将亡值

a + 5; // 这个结果是将亡值
a << 16; // 这个结果是将亡值

struct Size {
  float width;
  float hight;
};

Size getSize() {
  return Size { 800, 600 };
}

getSize(); // 这里返回的是将亡值
getSize().width = 900; // 编译错误:Expression is not assignable

引用

引用就是绑定关系,使表达式可以使用具体的名称来引用。

左值引用,就是一般的引用,相当于起了一个别名:

struct Size {
  float width;
  float hight;
};

void foo(Size &size) {
  size.width = 800;
}

Size s { 100, 100 };
foo(s); // 传进来的是 s 的引用,在函数内部相当于直接操作 s 本身

右值引用,也是绑定关系,但和左值的绑定有一点区别:是把右值使用移动的方式绑定到变量上。

int &&a = 6; // 把 6 移动给变量 a
a += 1; // 没有问题

C++ 有一个规定:凡具名者皆是左值:

struct Size {
  float width;
  float hight;
};

// 函数返回类型是引用,避免发生拷贝
Size& resize(Size &&size) {
  return size;
}

resize({ 800, 600 });

for_loop 循环中,使用 const auto &auto &&auto & 更具有普适性。

在使用循环枚举 STL 容器内容时,使用引用可以避免发生拷贝:

std::vector<struct Size> list {
    { 100, 100 },
    { 200, 200 },
    { 300, 300 }
};

// 每次都会发生拷贝
for (auto size: list) {
}

// 改为引用,避免发生拷贝
for (const auto &size: list) {
}

std::vector 存储的数据类型是 bool 时,它内部会使用位(bit)来存储,而不是为每个元素创建空间,这样做是为了节约内存而且提高存取速度。

std::vector<bool> l { true, false, true };

// 编译报错:Non-const lvalue reference to type '__bit_reference<...>' cannot bind to a temporary of type '__bit_reference<...>'
for (auto &b: l) {
}

这个时候返回的是右值,准确的说是将亡值,使用 auto & 指向右值是不对的。

for (const auto &b: l) {
}
// 或者使用右值引用
for (auto &&b: l) {
}

因为右值引用可以绑定将亡值(xvalue),并且不需要 const 限定符。

所以在循环中更通用的写法就诞生了:auto &&

(全文完)

参考资料

Lvalues and Rvalues (C++)

C++
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

Combine 与响应式编程

在 macOS 上使用 Finder 安装 IPCC (运营商配置文件)