Appearance
模板基础:声明形式、实例化、特化/偏特化、非类型参数
一、三类模板
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 的普通变量或函数(重定义)。
七、常见理解陷阱(背过就能答对)
- 函数模板不能偏特化,但可用重载近似;匹配顺序复杂。
- 模板实参推断不给显式时可空:
max<int>(1, 2.0)显式指定后隐式转换。 - dependent name(依赖名):
T::x需typename T::x(除非 C++20 auto)告诉编译器是类型;访问成员模板需templatedisambiguator。这是模板最难记的两处。 - 模板参数默认值只能放声明,不能放重复;类模板默认参放最后。
八、怎么向面试官讲「为什么 HPC 常把 size 做成模板」
回答:NTTP 让编译器在编译期知道循环边界 → 完全展开/unroll + 传播常量 → 配合 SIMD 无需运行期分支;换不同 size 只是换一次实例化(如 template<int BLK> void gemm(...) 按块特化)。这解释了许多高性能库把 int 维度/策略写进类型参数的动机。
九、自测推导
- 函数模板和重载同出现谁胜?
template<typename T> void f(T); template<typename T> void f(T*);对f(&x)哪个更特化优先生成?(指针版,约束更窄,更特化优先)enable_if/if constexpr之后才允许把 int 当参……(提示 SFINAE 主题在看)