一句话 :万能引用决定"能不能接住"左值和右值,
std::forward决定"怎么原样交出去"------两者是同一套机制的两半。
万能引用
cpp
template<class T>
void f(T&& x); // 万能引用(标准术语:forwarding reference)
int a;
const int b = 0;
f(a); // T = int&, x 的类型是 int&
f(1); // T = int, x 的类型是 int&&
f(b); // T = const int&, x 的类型是 const int&
它成立的条件只有两个:T 是模板参数 ,且 T 由实参推导 。推导结束后靠引用折叠收尾:T = int& 时 int& && 折叠为 int&,于是左值实参绑定到左值引用;T = int 时得到 int&&,右值实参绑定到右值引用。
值类别的信息被完整编码进了 T,这是后续一切的基础。
万能引用的写法
| 写法 | 是否万能引用 | 原因 |
|---|---|---|
template<class T> void f(T&&) |
✅ | 推导 + T&& |
template<class... A> void f(A&&...) |
✅ | 参数包展开同样是推导 |
auto&& x = expr; |
✅ | auto 扮演推导参数(泛型 lambda 同理) |
void f(const T&&) |
❌ | const 挡住了推导成引用的可能 |
void f(std::vector<T>&&) |
❌ | 形如 X<T>&&,只是普通右值引用 |
类模板成员 template<class T> class C { void f(T&&); } |
❌ | T 在类实例化时就已确定,不随调用推导 |
std::forward
cpp
void sink(int&);
void sink(int&&);
template<class T>
void relay(T&& v) {
sink(v); // ❌ v 是具名变量,永远是左值
sink(std::forward<T>(v)); // ✅ int& 进则 int& 出,int&& 进则 int&& 出
}
std::forward<T>(v) 本质是 static_cast<T&&>(v):T 是 int& 时折叠回左值,是 int 时得到右值。三条注意事项:
- 必须显式写
<T>,让编译器知道要还原成什么; T必须来自同一个转发引用的推导,不能手工指定;- 一个参数只转发一次,转发后不要再用它。