文章

条款24:区分万能引用与右值引用

万能引用能绑定左值和右值,类型依赖模板推导;右值引用只能绑定右值。

条款24:区分万能引用与右值引用

条款24:区分万能引用与右值引用

核心结论

判断条件结果
T&&并且发生类型推导万能引用(universal reference)
T&&但没有类型推导右值引用(rvalue reference)
const T&& / vector<T>&& 等非标准形式右值引用(rvalue reference)
auto&&,发生类型推导万能引用(universal reference)

万能引用(Universal Reference)

也称 转发引用(Forwarding Reference)

条件

  • 声明形式必须是 T&&(或 auto&&
  • 类型 T(或 auto)必须通过 类型推导 得出

特性

  • 可绑定左值右值
  • 可绑定 constvolatile 或任意组合
  • 初始化值决定实际类型(引用折叠规则起作用):
    • 初始化为左值 → 左值引用(T&
    • 初始化为右值 → 右值引用(T&&

示例

1
2
3
4
5
6
7
template<typename T>
void f(T&& param);  // T 通过调用 f 时推导出来 → 万能引用

auto&& val = expr;  // 万能引用(C++11 起)

// C++14 lambda 中的万能引用
auto lambda = [](auto&& x) { /* ... */ };

右值引用(Rvalue Reference)

条件

  • T&&,但类型没有推导,而是显式指定
  • 或者形式不是 T&&(如 const T&&, vector<T>&&

示例

1
2
3
4
5
6
7
8
void f(Widget&& param);         // 右值引用
Widget&& var = Widget();        // 右值引用

template<typename T>
void f(const T&& param);        // 右值引用(const 修饰失去万能性)

template<typename T>
void f(std::vector<T>&& param); // 右值引用(不是 T&&)

容易混淆的场景

错误地以为是万能引用:

1
2
3
4
5
6
7
8
9
10
template<typename T>
void f(const T&& param);  // 右值引用(const 破坏了万能性)

template<typename T>
void f(std::vector<T>&& param);  // 右值引用(不是 T&&)

template<typename T>
class MyVec {
    void push_back(T&& val); // T 已经由类模板实例化时指定 → 没有推导
};

真正的万能引用:

1
2
3
4
5
6
7
template<typename T>
void f(T&& param); // ✅ 万能引用

template<typename... Args>
void emplace_back(Args&&... args); // ✅ 万能引用参数包

auto&& val = getValue(); // ✅ auto&& 是万能引用

应用意义

  1. 完美转发
    • 只有万能引用能使用 std::forward 实现完美转发
  2. 接口设计
    • 右值引用用于移动语义(只接收右值)
    • 万能引用用于接收任意类型并完美转发
  3. 提高阅读/维护能力
    • 分辨 T&& 是哪种引用,避免误用
本文由作者按照 CC BY 4.0 进行授权