欢迎光临
我们一直在努力

引转移——理解引用折叠

文章目录

    • 理解引用折叠
      • 为什么需要引用折叠?
      • 核心规则
      • 发生语境
      • std::forward 如何依赖引用折叠
      • 通用引用的"通用性"解密

理解引用折叠

核心要点要点
1 引用折叠在模板实例化、auto 推导、typedef/using 别名、decltype 四种语境中发生
2 规则极简:左值引用总是赢——两个引用中只要有一个是左值引用,结果就是左值引用
3 通用引用之所以"通用",完全依赖于类型推导 + 引用折叠这两个机制的串联
4 std::forward 通过 static_cast<T&&> 配合引用折叠实现"条件转发":右值 → 右值,左值 → 左值
5 typedef T&& 中若 T 是引用类型,引用折叠会产生意外的结果(左值引用)——这就是它叫"折叠"的原因

为什么需要引用折叠?

C++ 禁止直接声明"引用的引用":

int x = 42;
auto& & rx = x; // ❌ 非法!不能声明"引用的引用"

但在四种语境中,引用的引用可能间接产生——此时编译器应用引用折叠将其归约为单一引用。


核心规则

📌 如果任一引用是左值引用,则结果为左值引用;否则(两者都是右值引用)结果为右值引用。

T& & → T& (左值引用的左值引用 → 左值引用)
T& && → T& (左值引用的右值引用 → 左值引用)
T&& & → T& (右值引用的左值引用 → 左值引用)
T&& && → T&& (右值引用的右值引用 → 右值引用)

🔑 记忆口诀:左值引用总是赢。只要两个引用中有一个是左值引用,结果就是左值引用。

发生语境

1️⃣模板实例化(最常见)

template<typename T>
void f(T&& param); // param 是通用引用

Widget w;
f(w); // T 推导为 Widget&
// 代入 → void f(Widget& && param)
// 折叠 → void f(Widget& param) ← 左值引用!

f(Widget()); // T 推导为 Widget
// 代入 → void f(Widget&& param) ← 右值引用(无折叠)

2️⃣auto 类型推导

Widget w;
auto&& w1 = w; // auto 推导为 Widget&
// Widget& && → Widget& (通过折叠)

auto&& w2 = Widget(); // auto 推导为 Widget
// Widget&& (无折叠)

// 这就是 auto&& 能绑定一切的原因:
// 左值 → auto=Widget& → Widget& && → Widget&
// 右值 → auto=Widget → Widget&& ← 引用折叠使 auto&& 成为"万能引用"

3️⃣typedef / using 别名

template<typename T>
class Widget {
public:
typedef T&& RvalueRefToT; // ⚠️ 命名有误导性
};

Widget<int&> w; // T 是 int&
// typedef int& && RvalueRefToT;
// 引用折叠 → typedef int& RvalueRefToT;
// 本想拿右值引用,结果拿到了左值引用!

4️⃣decltype

int x = 0;
decltype((x)) var; // decltype((x)) 是 int&(左值加括号 → 引用)
decltype(std::move(x)) var2; // int&&

std::forward 如何依赖引用折叠

template<typename T>
T&& forward(remove_reference_t<T>& param) noexcept
{
return static_cast<T&&>(param); // ← 引用折叠在此生效!
}

:::info 场景推演——传入左值 Widget:

:::

T = Widget&
forward<Widget&> 实例化:
Widget& && forward(…) { return static_cast<Widget& &&>(param); }
折叠:Widget& forward(…) { return static_cast<Widget&>(param); }
结果:返回左值引用 ✅

:::color1 场景推演——传入右值 Widget:

:::

T = Widget
forward<Widget> 实例化:
Widget&& forward(…) { return static_cast<Widget&&>(param); }
结果:返回右值引用 ✅(无折叠)

通用引用的"通用性"解密

通用引用之所以能绑定一切,是两个机制串联的结果:

1. 类型推导
左值 → T 推导为 T&
右值 → T 推导为 T

2. 引用折叠
T& && → T&
T && → T&&

📌_没有引用折叠,就没有通用引用;没有类型推导,引用折叠就无处生效。两者共同构成了 C++ 完美转发的基石。_

赞(0)
未经允许不得转载:171主机测评 » 引转移——理解引用折叠
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址