条款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
)必须通过 类型推导 得出
特性
- 可绑定左值、右值
- 可绑定
const
、volatile
或任意组合 - 初始化值决定实际类型(引用折叠规则起作用):
- 初始化为左值 → 左值引用(
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&& 是万能引用
应用意义
- 完美转发:
- 只有万能引用能使用
std::forward
实现完美转发
- 只有万能引用能使用
- 接口设计:
- 右值引用用于移动语义(只接收右值)
- 万能引用用于接收任意类型并完美转发
- 提高阅读/维护能力:
- 分辨 T&& 是哪种引用,避免误用
本文由作者按照 CC BY 4.0 进行授权