Skip to content

模板基础:声明形式、实例化、特化/偏特化、非类型参数

一、三类模板

cpp
// 函数模板
template <typename T> T max(T a, T b) { return a>b?a:b; }

// 类模板
template <typename T, typename Alloc = std::allocator<T>>
class MyVec { /*…*/ };

// 变量模板 (C++14)
template <typename T> inline constexpr bool is_integral_v = is_integral<T>::value;

二、模板的「实例化」本质(必答)

  • 模板本身不是类型/函数:它是生成代码的蓝图
  • 用到 MyVec<int> 时编译器按实参实例化出一份真实类型/函数。
  • 类模板在使用其成员(或完整对象)时按需实例化;函数模板按调用推断实参实例化。
  • 为什么实现放头文件? 因为每个翻译单元看到调用就要生成代码(除非显式实例化 .cpp 提供符号)。模板实现放 .cpp 会导致在别的 TU 链接不到(除非显式实例化 extern template)。
  • 代价:每 TU 独立实例化 → 二进制重复(用 extern template 减少;-fno-implicit-templates 等)。

三、特化(全特化)vs 偏特化

cpp
// 主模板
template <typename T, typename U> struct Pair { /*通用*/ };

// 偏特化(partial specialization):部分参数写死/缩小
template <typename U> struct Pair<int, U> { /*针对第一个int*/ };

// 全特化(explicit full specialization):全部实参给定
template <> struct Pair<int, char> { /*专属实现*/ };

要点

  • 类模板可偏特化 + 全特化函数模板只能全特化,不能偏特化(要用“函数重载”达到偏特化效果)——常考。
  • 选择顺序:全特化 > 更专科的偏特化 > 主模板,编译器取「最特化且可行」的匹配。

四、非类型参数(NTTP)

cpp
template <size_t N> struct FixedArray { T data[N]; };
FixedArray<64> a;               // N 是编译期常量
template <int W, int H> struct Mat;
Mat<64, 32> m;

可以做模板参数:整型、枚举、指针/reference、char*字面量(C++20 更宽:浮点/自定义对象)。NTTP = 编译期多态的工具(比如 kernel 按 tile 大小展开,避免运行期分支)。

五、模板参数包(variadic)

cpp
template <typename... Ts> auto sum(Ts... args);   // pack
// sizeof...(Ts) 元素个数
// C++17 fold: (args + ...)

六、模板与 ODR / 头文件包含

  • 类模板、inline 函数、内联变量、constexpr(C++17)多 TU 重复定义合法(weak symbols/vague linkage)——因此能放头文件。
  • 避免头文件放非 inline 的普通变量或函数(重定义)。

七、常见理解陷阱(背过就能答对)

  1. 函数模板不能偏特化,但可用重载近似;匹配顺序复杂。
  2. 模板实参推断不给显式时可空:max<int>(1, 2.0) 显式指定后隐式转换。
  3. dependent name(依赖名)T::xtypename T::x(除非 C++20 auto)告诉编译器是类型;访问成员模板需 template disambiguator。这是模板最难记的两处。
  4. 模板参数默认值只能放声明,不能放重复;类模板默认参放最后。

八、怎么向面试官讲「为什么 HPC 常把 size 做成模板」

回答:NTTP 让编译器在编译期知道循环边界 → 完全展开/unroll + 传播常量 → 配合 SIMD 无需运行期分支;换不同 size 只是换一次实例化(如 template<int BLK> void gemm(...) 按块特化)。这解释了许多高性能库把 int 维度/策略写进类型参数的动机。

九、自测推导

  1. 函数模板和重载同出现谁胜?
  2. template<typename T> void f(T); template<typename T> void f(T*);f(&x) 哪个更特化优先生成?(指针版,约束更窄,更特化优先)
  3. enable_if/if constexpr 之后才允许把 int 当参……(提示 SFINAE 主题在看)

C++ 面试八股 · VitePress 版