Skip to content

模板推导规则(引用折叠、auto、多个参数包)+ 经典题

元编程面试的“测脑回路”区域。目标是能对着一小段推导题说出最终类型,并善用 auto/decltype/引用折叠/完美转发。

一、函数模板推导 vs auto 推导(几乎一致)

两者一致特例:auto 可绑花括号 initializer_list,模板不可。

模板推导核心三条(按形参形式)

cpp
template<typename T> void f1(T);        // 传值 → 剥掉引用、剥顶层 const/volatile
template<typename T> void f2(T&);       // 引用 → 不剥(保留底层)… 但不剥 const
template<typename T> void f3(T&&);      // 万能 → 引用折叠规则 + 转发引用

举例:

cpp
const int ci = 1;
f1(ci);       // T=int      (const 被剥)
f2(ci);       // T=const int(保 const,因为我们用的是 ci 的引用)
int x=0;
f3(x);        // T=int&     (左值 → T=int&,折叠成 int&)
f3(42);       // T=int      (右值 → T=int&&)

二、引用折叠(rules)简记

T& && → T&T&& & → T&;一旦出现 & 结果就是 &;只有 && && 才是 &&

这就是万能引用 T&& 能接左值的原因:绑左值时 T 推导为 int&,形参 int& &&折叠成 int&

三、完美转发(forward)配 forwarding reference

cpp
template <typename T, typename... Args>
T make_(Args&&... args) {
    return T(std::forward<Args>(args)...);   // 逐个按原左右值传
}

要区分 std::move(无条件右值化)与 std::forward(保留原值类别的条件转发)——见 modern-cpp/move-semantics。

四、auto 对应推导(const/引用剥除表)

  1. auto x = expr;——按传值规则(剥引用与顶层 const)。
  2. auto& x = expr;——引用形式(不剥 const;绑定 const 对象得 const auto&)。
  3. auto&& x——万能。
  4. 数组/函数:auto 退化指针;auto& 保留数组。
  5. decltype(expr)原样;括号会影响:decltype((x)) 因 (x) 是左值表达式 → 得 T&
cpp
int i; const int& cri = i;
auto a = cri;    // int(剥引用+const)
auto& r = cri;   // const int&
decltype(cri) d; // const int&

五、让面试官直呼扎实的几张表

表 A:auto 变量

| 声明 | i 为 const int& | 43(int) | | auto v | int | int | | auto& v | const int& | 非法(不绑右值到左值引用) | | const auto& v | const int& | const int&(绑定临时) | | auto&& v | const int& | int&& |

表 B:f T 推导同 auto。

表 C:decltype(x) vs decltype((x)) vs auto ——剥 vs 原样 vs 有括号

六、pack 推导 / 非推导语境

  • template<typename T> void f(std::vector<T>) 不能由 f(v) 推导出 T,除非能匹配(从实参匹配其实能,因为 T 在模板参数里)。真正“不能推导”常指:出现在非推导语境(如 typename T::Xconst T* 某些 case、lambda 捕获?实参前),需显式指定。
  • 例:template<typename T> void f(typename T::type)——T 从实参无法推导出 typename U::type 里的 U → 需显式。

七、经典推导题速刷(盖答案)

cpp
template <typename T> void F(T);            // p1: T=
template <typename T> void G(T&);            // p2:
template <typename T> void H(T&&);           // p3:

int x;        F(x) ____  G(x) ____  H(x) ____
const int cx; F(cx) ____ G(cx)____ H(cx)=const int&
int* p;       F(p) ____  G(p)=int*& H(p)=int*&
int arr[4];   F(arr)=int* G(arr)=int(&)[4] H(arr)=int(&)[4]

答案参考:F:X 一律剥 cv/引用(值);G 保留引用形态;H 按折叠。数组在“引用形式”保留数组类型、在“值形式”退化指针。

八、如何答题让考官看出“真懂”

  1. 先说“模板推导和 auto 机制一致”,建立框架。
  2. 抓住“剥引用/顶层 cv只在传值时”,这是 90% 陷阱根源。
  3. 引用折叠只用一条结论“出现 & 即 &”。
  4. 区别 auto(剥) 与 decltype(原样) 与 decltype((x))(变 T&)。

C++ 面试八股 · VitePress 版