1. 模板基础概念
(1) 什么是模板?
模板是 C++ 泛型编程的核心,允许编写与类型无关的代码,编译器在编译时根据具体类型生成对应版本。
核心目标:避免重复代码,提高复用性。
(2) 确定参数模板(Fixed Template)
- 语法:使用固定数量的类型参数。
template<typename T> // 单个类型参数 T max(T a, T b) { return (a > b) ? a : b; } template<typename K, typename V> // 多个类型参数 class Pair { K key; V value; }; - 特点:
- 参数数量固定(如
std::vector<T>)。 - 适用于已知参数数量的场景。
- 参数数量固定(如
(3) 可变参数模板(Variadic Template)
- 语法:使用
...表示任意数量的类型参数。template<typename... Args> // Args 是模板参数包 void print(Args... args); // args 是函数参数包 - 特点:
- 处理不定数量参数(如
std::tuple<Args...>)。 - 常配合递归或折叠表达式展开参数包。
- 处理不定数量参数(如
2. 主模板(Primary Template)
(1) 定义
主模板是模板的默认实现,提供通用逻辑,后续可通过特化定制行为。
关键点:
- 必须先声明主模板,才能特化。
- 可以是空实现(如
std::tuple的主模板仅为声明)。
(2) 作用
- 提供默认行为:通用逻辑(如
std::vector<T>的动态数组实现)。 - 支持特化扩展:作为全特化/部分特化的基础。
- 递归终止条件:可变参数模板中标记递归结束(如
Tuple<>)。
(3) 示例
// 主模板(通用版本)
template<typename T>
struct Box { T value; };
// 可变参数主模板(通常为空)
template<typename... Args>
struct Tuple {};
3. 实例化(Instantiation)
(1) 定义
编译器根据模板生成具体类型代码的过程。
- 隐式实例化:通过代码调用自动触发(如
std::vector<int>)。 - 显式实例化:手动指定类型生成代码(如
template class std::vector<int>;)。
(2) 过程
template<typename T>
T add(T a, T b) { return a + b; }
int sum = add(3, 5); // 隐式实例化 add<int>
编译器生成:
int add<int>(int a, int b) { return a + b; }
4. 特化(Specialization)
(1) 定义
为特定类型定制模板行为,覆盖主模板的默认实现。
分类:
- 全特化:完全指定所有模板参数(函数/类模板均支持)。
- 部分特化:仅指定部分参数(仅类模板支持)。
(2) 为什么需要特化?
- 优化性能:为特定类型(如
const char*)提供高效实现。 - 修正行为:处理主模板不支持的场景(如指针类型)。
- 递归终止:可变参数模板的基准情形。
(3) 示例
全特化
template<>
const char* max<const char*>(const char* a, const char* b) {
return strcmp(a, b) > 0 ? a : b; // 定制字符串比较
}
部分特化(类模板)
template<typename T>
class Box<T*> { // 针对指针类型的部分特化
T* ptr;
};
5. 面试常见问题
(1) 基础问题
-
模板的作用是什么?
- 实现泛型编程,避免重复代码,提高类型安全性。
-
函数模板和类模板的区别?
- 函数模板生成函数,类模板生成类;函数不支持部分特化。
-
typename和class在模板中的区别?- 在模板参数中完全等价,但
typename还可用于声明嵌套类型。
- 在模板参数中完全等价,但
(2) 高级问题
- 什么是 SFINAE?如何利用它?
- “Substitution Failure Is Not An Error”:模板替换失败时跳过而非报错,常用于条件编译(如
std::enable_if)。
- “Substitution Failure Is Not An Error”:模板替换失败时跳过而非报错,常用于条件编译(如
-
可变参数模板如何实现递归展开?
- 通过递归调用 + 基准情形(如
Tuple<>),或 C++17 的折叠表达式。
- 通过递归调用 + 基准情形(如
-
如何避免模板代码膨胀?
- 使用显式实例化、共同基类提取代码,或
extern template声明。
- 使用显式实例化、共同基类提取代码,或
(3) 实战问题
-
写一个
std::tuple的简化实现- 需展示主模板、递归特化、基准情形。
-
为什么
std::vector<bool>是特化的?- 优化空间(用位存储),但导致代理迭代器问题。
-
解释
template<>的作用- 标记全特化,表示“完全指定模板参数,无需泛型逻辑”。
总结
| 概念 | 核心要点 |
|---|---|
| 模板基础 | 泛型编程,支持确定参数和可变参数。 |
| 主模板 | 默认实现,支持特化扩展,可变参数中用于递归终止。 |
| 实例化 | 编译器生成具体代码(隐式/显式)。 |
| 特化 | 定制特定类型行为(全特化/部分特化),解决默认实现的不足。 |
| 面试重点 | SFINAE、可变参数展开、代码优化、std::vector<bool> 特化问题等。 |
掌握模板机制是 C++ 高级开发的必备技能,理解其底层原理能显著提升代码设计能力!