左值与右值

C++ 中值有两个独立的属性:有地址的值(gvalue)和可以被移动的值(rvalue)

  1. 有地址 + 不能移动 = 左值(lvalue)

  2. 有地址 + 可以移动 = 将亡值(xvalue), 包括: 右值引用类型的返回值,比如 std::move(x)

  3. 没有地址 + 可以移动 = 纯右值(prvalue), 包括:

    • 字面量,比如 42
    • 临时对象,比如 1 + 2
    • this 指针
  4. 没有地址 + 不能移动, 目前没有用到

总之, 一个值是左值还是右值,取决于它的地址是否可以被获取。大多数时候只需要区分一个值是左值还是右值即可。

右值引用

右值引用是 C++11 引入的一种引用类型,用于表示对右值的引用。右值引用可以绑定到右值,但不能绑定到左值。右值引用通常用于实现移动语义和完美转发。

  • 右值引用只能绑定到右值上,比如 int &&
  • 左值引用只能绑定到左值上,比如 int &
  • const 的左值引用可以绑定到左值或右值上,比如 const int &

移动构造函数和移动赋值运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
class Widget {
private:
int i{0};
string s{};
unique_ptr<int> pi{};

public:
// Move constructor
Widget(Widget &&w) = default;

// Move assignment operator
Widget &operator=(Widget &&w) = default;
};

移动操作和异常安全

  • 移动操作一般不分配新资源,因此不会抛出异常
  • 如果移动操作不抛异常,必须注明 noexcept

通用引用和引用折叠

通用引用

通用引用是一种既可以绑定到左值,又可以绑定到右值的引用类型,其本质是一个模板参数化的右值

1
2
3
4
template <typename T>
void func(T&& arg) { // T&& 是通用引用
// arg 既可以接受左值,也可以接受右值
}

引用折叠

引用折叠规则:

  1. 左值引用(&)优先级高于右值引用(&&)。

  2. 以下是所有的折叠情况:

    • T& & → T&
    • T& && → T&
    • T&& & → T&
    • T&& && → T&&

remove_reference

std::remove_reference 是 C++ 标准库中的一个类型特性(type trait),定义在头文件 中。它的主要作用是移除类型中的引用部分(包括左值引用 & 和右值引用 &&),返回原始类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
struct remove_reference {
typedef T type;
};

template <typename T>
struct remove_reference<T&> {
typedef T type;
};

template <typename T>
struct remove_reference<T&&> {
typedef T type;
};

常用场景: 类型推导、模板元编程、完美转发等。

总结

  • std::remove_reference 是一个编译期工具,通过模板特化对类型进行处理。
  • 它不会在运行时产生临时变量,也不会涉及任何值类别的转换。
  • 编译器只会根据类型推导规则替换类型,例如将 int& 转换为 int,最终生成相应的代码。
  • 不会去掉 const 和 volatile 修饰符