# C++左值与右值

## 说明

* 这一部分内容只是帮助理解 C++(11) 中左值与右值的概念。
* 在编程实践中，因为**编译器优化**的存在，特别是其中的**返回值优化**（Return Value Optimization, RVO）使你不需要额外关注左值与右值的区别，像 C++(03) 一样编程即可。

  > [C++11 rvalues and move semantics confusion (return statement)](https://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion-return-statement) - Stack Overflow
* 除非你在进行库的开发，特别是涉及模板元编程等内容时，需要实现[移动构造函数](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#移动构造函数)（move constructor），或者[完美转发](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#完美转发)

## Index

* [小结](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#小结)
  * [左值引用类型 与 右值引用类型](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#左值引用类型-与-右值引用类型)
  * [当发生自动类型推断时，`T&&` 也能绑定左值](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#当发生自动类型推断时t-也能绑定左值)
  * [如何快速判断左值与右值](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#如何快速判断左值与右值)
  * [引用折叠规则](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#引用折叠规则)
  * [`move()` 与 `forward()`](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#move-与-forward)
* [左值与右值的本质](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#左值与右值的本质)
  * [左值、消亡值、纯右值](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#左值消亡值纯右值)
* [右值引用的特点](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#右值引用的特点)
  * [右值引用延长了临时对象的生命周期](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#右值引用延长了临时对象的生命周期)
  * [利用右值引用避免临时对象的拷贝和析构](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#利用右值引用避免临时对象的拷贝和析构)
  * [右值引用类型绑定的一定是右值，但 `T&&` 可能不是右值引用类型](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#右值引用类型绑定的一定是右值但-t-可能不是右值引用类型)
  * [当发生自动类型推断时，`T&&` 是未定的引用类型](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#当发生自动类型推断时t-是未定的引用类型)
* [常量（左值）引用](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#常量左值引用)
* [返回值优化 RVO](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#返回值优化-rvo)
* [移动语义](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#移动语义)
  * [深拷贝带来的问题](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#深拷贝带来的问题)
  * [移动构造函数](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#移动构造函数)
  * [移动语义 与 `move()`](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#移动语义-与-move)
  * [`move()` 的本质](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#move-的本质)
  * [`move()` 的原型 TODO](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#move-的原型-todo)
* [完美转发](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#完美转发)
  * [`forward<T>()` 实现完美转发](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#forwardt-实现完美转发)
  * [`forward<T>()`的原型 TODO](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#forwardt的原型-todo)
* [Reference](/artificial-intelligence/programming/cppc-zuo-zhi-yu-you-zhi.md#reference)

## 小结

### 左值引用类型 与 右值引用类型

```cpp
T   t1;         // 类型 T
T&  t2 = t1;    // T&  表示 T 的左值引用类型，t2 是左值引用类型的变量，它引用的是一个左值
T&& t3 = T();   // T&& 表示 T 的右值引用类型，t3 是右值引用类型的变量，它引用的是一个右值

T&  t4 = T();   // err: 左值引用 不能绑定一个 右值
T&& t5 = t1;    // err: 右值引用 不能绑定一个 左值

const T& t6 = t1;   // const T& 表示 T 的常量（左值）引用
const T& t7 = T();  // 常量引用类型是“万能”的引用类型

// 不能把 常量类型 绑定到 非常量引用类型 上
T&& t8 = t6;        // err: 不能把常量类型绑定到 右值引用类型
T&  t9 = t6;        // err: 也不能把常量类型绑定到 左值引用类型
```

> 这里的变量 t1\~t9 都是左值，因为它们都有名字

### 当发生自动类型推断时，`T&&` 也能绑定左值

```cpp
template<typename T>    // 模板元编程
void foo(T&& t) { }     // 此时 T&& 不是右值引用类型，而是未定引用类型

void bar(int&& v) { }   // 非模板编程，int&& 是明确的右值引用类型

foo(10);        // OK: 未定引用类型 t 绑定了一个右值
bar(10);        // OK: 右值引用类型 v 绑定了一个右值

int x = 10;
foo(x);         // OK:  未定引用类型 t 绑定了一个左值
bar(x);         // err: 右值引用类型 v 不能绑定一个左值

int&&  p = x;   // err
auto&& t = x;   // OK
```

* 此时，`T&&` 就不再是右值引用类型，而是**未定引用类型**

### 如何快速判断左值与右值

* **能被** `&` **取地址的就是左值**

  ```
  int foo;               // foo 是一个左值
  cout << &foo;          // 可以被取地址
  foo = foo + 5;         // foo + 5 是一个左值
  cout << &(foo+5);      // err
  cout << &1;            // err
  ```

  * 多数常数、字符等字面量都是右值，但**字符串是左值**
  * 虽然**字符串字面量**是左值；但它是 **const 左值**（只读对象），所以也不能对它赋值

    ```cpp
    cout << &'a';     // err: lvalue required as unary '&' operand
    cout << &"abc";   // OK, 可以对字符串取地址
    "abc" = "cba";    // err: assignment of read-only location
    ```

    > 为什么字符串字面量是对象？——节省内存，同一份字符串字面量引用的是同一块内存
* 所有的具名变量或对象都是左值，而匿名变量/临时变量则是右值
  * 匿名变量/临时变量的特点是表达式结束后就销毁了

    ```cpp
    int i = 5;               // int 型字面量
    auto f = []{return 5;};  // lambda 表达式
    ```

### 引用折叠规则

1. 所有的右值引用叠加到右值引用上仍然还是一个右值引用。（`T&& && 变成 T&&`）&#x20;
2. 所有的其他引用类型之间的叠加都将变成左值引用。 （`T& &, T& &&, T&& & 都变成 T&`）
3. 对常量引用规则一致
4. 示例

   ```cpp
   typedef int &  lRef;
   typedef int && rRef;

   typedef const int &  lcRef;
   typedef const int && rcRef;

   int main() {
       int a = 10;

       // 左值引用
       lRef    b = a;    // T&
       lRef &  c = a;    // T& &
       lRef && d = a;    // T& &&
       rRef &  e = a;    // T&& &

       // 右值引用
       rRef    f = 10;   // T&&   
       rRef && g = 10;   // T&& &&

       // 左值引用
       lcRef     b2 = a;    // const T& 
       lcRef &   c2 = a;    // const T& &
       lcRef &&  d2 = a;    // const T& &&
       rcRef &   e2 = a;    // const T&& & 

       // 右值引用
       rcRef     f2 = 10;   // const T&&
       rcRef &&  g2 = 10;   // const T&& &&

       return 0;
   }
   ```

### `move()` 与 `forward()`

* `move()` 的主要作用是将一个左值转为 xvalue（右值）, 其实现本质上是一个 `static_cast<T>`
* `forward()` 主要用于实现完美转发，其作用是将一个类型为（左值/右值）引用的左值，转化为它的类型所对应的值类型（左值/右值）

  > 觉得难以理解的话，就继续看下去吧

## 左值与右值的本质

* 左值表示是“**对象**”（object），右值表示“**值**”（value）——**“对象”内存储着“值”**
* 左值 `->` 右值的转换可看做“读取对象的值”（reading the value of an object）
* 其他说法：
  * 左值是可以作为内存单元**地址的值**；右值是可以作为内存单元**内容的值**
  * 左值是内存中持续存储数据的一个地址；右值是临时表达式结果

### 左值、消亡值、纯右值

* C++11 开始，表达式一般分为三类：左值（lvalue）、消亡值（xvalue）和纯右值（prvalue）；
* 其中左值和消亡值统称**泛左值**（glvalue）；

  消亡值和纯右值统称**右值**（rvalue）。

## 右值引用的特点

### 右值引用延长了临时对象的生命周期

```
int i = getI();  // getI() 会返回一个 int 型的临时变量
T&& t = getT();  // t 是一个右值引用
                  // getT() 同样返回一个临时变量，但是该临时变量被“引用”了
                  // 因此生命周期得到了延长
```

* `getI()` 和 `getT()` 都返回一个临时变量，但是 `getT()` 产生的临时变量不会在表达式结束后就马上销毁，而是会被“续命”——它的声明周期将和它的**引用类型变量** `t` 一样长。

### 利用右值引用避免临时对象的拷贝和析构

```cpp
  int g_constructCount=0;
  int g_copyConstructCount=0;
  int g_destructCount=0;

  struct A {
      A(){              // 基本构造
          cout<<"construct: "<<++g_constructCount<<endl;    
      }

      A(const A& a) {   // 拷贝构造
          cout<<"copy construct: "<<++g_copyConstructCount <<endl;
      }

      ~A() {            // 析构
          cout<<"destruct: "<<++g_destructCount<<endl;
      }
  };

  A getA() {
      A a;            // 第一次构造
      return a;
      // return A();  // 等价，分开写是为了便于说明
  }

  int main() {
      A a2 = getA();    // 非右值引用
      A&& a3 = getA();  // 右值引用
      return 0;
  }
```

* 非右值引用，关闭返回值优化

  ```cpp
  construct: 1        // 第一次构造，getA() 中的局部变量 a
  copy construct: 1   // 第二次构造，将 a 复制给一个临时变量
  destruct: 1           // 析构局部变量 a
  copy construct: 2   // 第三次构造，将临时变量复制给 a2
  destruct: 2           // 析构临时变量
  destruct: 3           // 程序结束，析构变量 a2
  ```
* 右值引用，关闭返回值优化

  ```cpp
  construct: 1        // 第一次构造，getA() 中的局部变量 a
  copy construct: 1   // 第二次构造，将 a 复制给一个临时变量
                      // 右值引用 a3 延长了临时变量的声明周期，使其没有马上被析构
  destruct: 1           // 析构局部变量 a
  destruct: 2           // 程序结束，析构变量 a3
  ```

  > 利用常量引用也能避免临时对象的拷贝与析构 -> [常量（左值）引用](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#常量左值引用)
  >
  > 返回值优化做的更彻底 -> [返回值优化 RVO](https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/pages/-L_Bw9InUVg-BleP0NAR#返回值优化-rvo)

### 右值引用类型绑定的一定是右值，但 `T&&` 可能不是右值引用类型

```cpp
int&& v1 = 1;   // OK: v1 是右值引用类型，且 1 是右值
int&& v2 = v1;  // err: v2 是右值引用类型，但 v1 是左值
```

### 当发生自动类型推断时，`T&&` 是未定的引用类型

* `T&& t` 在发生**自动类型推断**时，是未定的引用类型

  * 比如模板元编程，auto 关键字等
  * 如果 `t` 被一个左值初始化，它就是一个左值；如果 `t` 被一个右值初始化，它就是一个右值

    \`\`\`Cpp

    template    // 模板元编程

    void foo(T&& t) { }     // 此时 T&& 不是右值引用类型，而是未定引用类型

  foo(10); // OK: 未定引用类型 t 绑定了一个右值

  int x = 10; foo(x); // OK: 未定引用类型 t 绑定了一个左值

  int&& p = x; // err auto&& t = x; // OK \`\`\`
* 仅当发生自动类型推导时（模板编程，auto 关键字），`T&&` 才是未定引用类型

  ```cpp
  void bar(int&& v) { }   // 非模板编程，int&& 是明确的右值引用类型

  bar(10);        // OK: 右值引用类型 v 绑定了一个右值

  int x = 10;
  bar(x);         // err: 右值引用类型 v 不能绑定一个左值
  ```

## 常量（左值）引用

* 右值引用是 C++11 引入的概念
* 在 C++11 前，是如何避免临时对象的拷贝和析构呢？——利用**常量左值引用**

  ```cpp
  const A& a = getA();  // OK: 常量左值引用可以接受右值
  ```
* 常量左值引用是一个“万能”的引用类型，可以接受左值、右值、常量左值和常量右值
* 普通的左值引用不能接受右值

  ```cpp
  A& a = getA();        // err: 非常量左值引用只能接受左值
  ```

## 返回值优化 RVO

* 利用右值引用可以避免临时对象的拷贝可析构
* 但编译器的返回值优化（Return Value Optimization, RVO）做得“更绝”，直接回避了所有拷贝构造

  ```cpp
  int g_constructCount=0;
  int g_copyConstructCount=0;
  int g_destructCount=0;

  struct A {
      A(){              // 基本构造
          cout<<"construct: "<<++g_constructCount<<endl;    
      }

      A(const A& a) {   // 拷贝构造
          cout<<"copy construct: "<<++g_copyConstructCount <<endl;
      }

      ~A() {            // 析构
          cout<<"destruct: "<<++g_destructCount<<endl;
      }
  };

  A getA() {
      A a;            // 第一次构造
      return a;
      // return A();  // 等价，分开写是为了便于说明
  }

  int main() {
      A aa = getA();    // 第二次构造：把 a 复制给一个临时变量，
                        // 第三次构造：把临时变量复制给 aa
                        // 开启优化后，相当于直接把 a “改名”成 aa 了，所以只有一次构造
      return 0;
  }
  ```

  * 关闭编译器优化的结果

    ```cpp
    construct: 1        // 第一次构造，getA() 中的局部变量 a    
    copy construct: 1   // 第二次构造，将 a 复制给一个临时变量
    destruct: 1           // 析构局部变量 a
    copy construct: 2   // 第三次构造，将临时变量复制给 aa
    destruct: 2           // 析构临时变量
    destruct: 3           // 程序结束，析构变量 aa
    ```
  * 开启编译器优化

    ```cpp
    construct: 1        // 构造局部变量 a，在编译的优化下，相当于直接将 a “改名” aa
    destruct: 1         // 程序结束，析构变量 aa
    ```
  * 返回值优化并不是 C++ 的标准，是各编译器优化的结果，但是这项优化并不复杂，所以基本流行的编译器都提供

## 移动语义

### 深拷贝带来的问题

* 带有**堆内存**的类，必须提供一个深拷贝构造函数，以避免“指针悬挂”问题

  > 所谓指针悬挂，指的是两个对象内部的成员指针变量指向了同一块地址，析构时这块内存会因被删除两次而发生错误
  >
  > \`\`\`Cpp class A { public: A(): m\_ptr(new int(0)) { // new 堆内存 cout << "construct" << endl; }

  ```
    A(const A& a):m_ptr(new int(*a.m_ptr)) {  // 深拷贝构造函数
        cout << "copy construct" << endl;
    }

    ~A(){
        // cout << "destruct" << endl;
        delete m_ptr;   // 析构函数，释放堆内存的资源
    }
  ```

  private: int\* m\_ptr; // 成员指针变量 };

  A getA() { return A(); }

  int main() { A a = getA(); return 0; }

  ````
    - 输出（关闭 RVO）
    ```Cpp
    construct
    copy construct
    copy construct
  ````

  > 如果不关闭 RVO，只会输出 `construct`
* 提供深拷贝能够保证程序的正确性，但会带来额外的性能损耗——临时对象也会申请一块内存，然后又马上被销毁了；如果堆内存很大的话，这个性能损耗是不可忽略的
* 对于临时对象而言，深拷贝不是必须的
* 利用右值引用可以避免无谓的深拷贝——移动拷贝构造函数

### 移动构造函数

* 相比上面的代码，这里只多了一个移动构造函数——一般会同时提供拷贝构造与移动构造

  ```cpp
  class A {
  public:
      A(): m_ptr(new int(0)) {                    // new 堆内存
          cout << "construct" << endl;
      }

      A(const A& a): m_ptr(new int(*a.m_ptr)) {   // 深拷贝构造函数
          cout << "copy construct" << endl;
      }

      A(A&& a): m_ptr(a.m_ptr) {                  // 移动构造函数
          a.m_ptr = nullptr;      // 把参数对象的指针指向 nullptr
          cout << "move construct" << endl;
      }

      ~A(){
          // cout << "destruct" << endl;
          delete m_ptr;   // 析构函数，释放堆内存的资源
      }
  private:
      int* m_ptr;         // 成员指针变量
  };

  A getA() {
      return A();
  }

  int main() {
      A a = getA();
      return 0;
  }
  ```
* 输出（关闭返回值优化）

  ```cpp
  construct
  move construct        // 没有调用深拷贝，值调用了移动构造函数
  move construct
  ```

  > 如果不关闭 RVO，只会输出 `construct`
* 这里没有自动类型推断，所以 `A&&` 一定是右值引用类型，因此所有**临时对象**（右值）会匹配到这个构造函数，而不会调用深拷贝
* 对于临时对象而言，没有必要调用深拷贝
* 这就是所谓的**移动语义**——右值引用的一个重要目的就是为了支持移动语义

### 移动语义 与 `move()`

* 移动语义是通过右值引用来匹配临时值，从而避免深拷贝
* 利用 `move()` 方法，可以将普通的左值转化为右值来达到**避免深拷贝**的目的

  ```cpp
  class A {
  public:
      A(): m_ptr(new int(0)) {                    // new 堆内存
          cout << "construct" << endl;
      }

      A(const A& a): m_ptr(new int(*a.m_ptr)) {   // 深拷贝构造函数
          cout << "copy construct" << endl;
      }

      A(A&& a): m_ptr(a.m_ptr) {                  // 移动构造函数
          //a.m_ptr = nullptr;      // 为了实验，这里没有把参数对象的指针指向 nullptr
          cout << "move construct" << endl;
      }

      ~A(){
          // cout << "destruct" << endl;
          delete m_ptr;   // 析构函数，释放堆内存的资源
      }

      int get_data() {
          return *m_ptr;
      }

      void set_data(int v) {
          *m_ptr = v;
      }
  private:
      int* m_ptr;         // 成员指针变量
  };

  int main() {
      A a1;                                 // construct
      a1.set_data(1);
      cout << a1.get_data() << endl;  // 1

      A a2 = a1;                            // copy construct
      cout << a2.get_data() << endl;  // 1
      a2.set_data(2);
      cout << a2.get_data() << endl;  // 2
      cout << a1.get_data() << endl;  // 1

      A a3 = move(a1);                      // move construct
      a3.set_data(3);
      cout << a3.get_data() << endl;  // 3
      cout << a1.get_data() << endl;  // 3: 因为没有深拷贝，指向的是同一块地址
      return 0;
  }
  ```

  * 运行结果

    ```cpp
    construct
    1
    copy construct
    1
    2
    1
    move construct
    3
    3
    ```
* STL 容器的移动语义

  ```cpp
  {
      list<string> tokens;
      //省略初始化...
      list<string> t = tokens;    // 这里存在深拷贝 
  }
  list<string> tokens;
  list<string> t = move(tokens);  // 这里没有深拷贝
  ```

  * C++11 中所有的容器都实现了移动语义

### `move()` 的本质

* `move()` 实际上并没有移动任何东西，它唯一的功能是将一个左值**强制转换**为一个右值引用
* 如果没有对应的移动构造函数，那么使用 `move()` 仍会发生深拷贝，比如基本类型，定长数组等
* 因此，`move()` 对于含有资源（堆内存或句柄）的对象来说更有意义。

### `move()` 的原型 TODO

> [c++11 中的 move 与 forward - twoon](https://www.cnblogs.com/catch/p/3507883.html) - 博客园

## 完美转发

* 右值引用的引入，使函数可以根据值的类型（左值或右值）进行不同的处理
* 于是又引入了一个问题——如何正确的传递参数，保持参数作为左值或右值的特性
* 转发失败的例子：

  ```cpp
  void processValue(int& a)  { cout << "lvalue" << endl; }
  void processValue(int&& a) { cout << "rvalue" << endl; }

  template <typename T>
  void forwardValue(T&& val) {
      processValue(val);      // 因为 val 本身是一个左值
                              // 所以无论 val 是左值引用类型还是右值引用类型的变量
                              // 都只会调用 processValue(int& a)
  }

  int main() {
      int i = 1;
      forwardValue(i);    // 传入一个左值
                          // val 会被推断为是一个左值引用类型

      forwardValue(1);    // 传入一个右值
                          // 虽然 val 会被推断为是一个右值引用类型，但它本身是一个左值
      return 0;
  }
  ```

  * 输出

    ```cpp
    lvalue
    lvalue
    ```
  * 无论传入的是左值还是右值，val 都是一个左值

### `forward<T>()` 实现完美转发

> 这里写的不够详细，有时间在整理
>
> * 在函数模板中，`T&&` 实际上是未定引用类型，它是可以得知传入的对象是左值还是右值的
> * 这个特性使其可以成为一个参数的路由，利用 `forward()` 实现完美转发
> * `std::forward<T>()` 可以保留表达式作为“对象”（左值）或“值”（右值）的特性
>
>   \<!-- \`\`\`Cpp
>
>   int&& a = 1;

cout << \&a; // OK: 虽然 a 是一个右值引用类型的变量，但它本身是一个左值 cout << \&forward(a); // err: taking address of xvalue (rvalue reference)

````
- 利用 `std::forward<T>()` 实现完美转发：
  > 不可以用变量接收 `forward<T>()` 的返回值，因为所有具名变量都是左值
  ```Cpp
  void processValue(int& a)  { cout << "lvalue" << endl; }
  void processValue(int&& a) { cout << "rvalue" << endl; }

  template <typename T>
  void forwardValue(T&& val) {
      processValue(forward<T>(val));   // 利用 forward 保持对象的左右值特性

      // 必须把 forward<T>(val) 打包作为参数，否则都达不到完美转发的目的
      // auto v = forward<T>(val);
      // processValue(v);

      // auto&& v = forward<T>(val);
      // processValue(v);
  }

  int main() {
      int i = 1;
      forwardValue(i);    // 传入一个左值

      forwardValue(1);    // 传入一个右值
      return 0;
  }
````

* 输出

  ```cpp
  lvalue
  rvalue
  ```
* 正确实现了转发

### `forward<T>()`的原型 TODO

## Reference

* [The lvalue/rvalue metaphor](https://josephmansfield.uk/articles/lvalue-rvalue-metaphor.html) — Joseph Mansfield
* [关于C++左值和右值区别有没有什么简单明了的规则可以一眼辨别？](https://www.zhihu.com/question/39846131/answer/85277628) - 知乎
* [从4行代码看右值引用 - qicosmos(江南)](https://www.cnblogs.com/qicosmos/p/4283455.html) - 博客园&#x20;
* [c++11 中的 move 与 forward - twoon](https://www.cnblogs.com/catch/p/3507883.html) - 博客园&#x20;
* [左值右值的一点总结 - twoon](http://www.cnblogs.com/catch/p/5019402.html) - 博客园&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fantasy-jxf.gitbook.io/artificial-intelligence/programming/cppc-zuo-zhi-yu-you-zhi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
