C++ 模板全览:从“非特化”到“全特化 / 偏特化”的完整原理与区别
C++ 模板全览:从“非特化”到“全特化 / 偏特化”的完整原理与区别
一句话总览:
模板是 编译期代码生成器;
非特化 给出“通用配方”,全特化 / 偏特化 给出“定制配方”;
抉择全程 静态完成,运行时 零开销。
1 模板本质:编译期的“宏++”
维度 | 解释 |
---|---|
编译期 | 模板代码本身 不会 产生任何机器指令。 |
实例化 | 编译器看到实参后,把占位符替换成具体类型,生成真正的函数 / 类。 |
零开销 | 生成的函数与普通函数 完全一样:没有虚表、没有分支。 |
1.1 模板是什么?
- 编译期代码生成器
只在实例化(instantiation)时把占位符换成具体类型,生成真正的函数或类。 - 零运行时开销
没有虚表、没有分支,与普通代码一样直接call
地址。
1.2 两大基本形态
形态 | 关键词 | 占位符 | 典型语法 |
---|---|---|---|
函数模板 | template<typename T> | 形参 / 返回值 | T add(T a, T b) |
类模板 | template<typename T> | 成员变量 / 成员函数 | Vector<T> |
2 三种形态总览
形态 | 关键字 | 占位符 | 匹配优先级 | 典型用途 |
---|---|---|---|---|
非特化(主模板) | template<typename T> | 完整占位 | 最低 | 通用实现 |
全特化 | template<> | 无占位 | 最高 | 对某个具体类型做完全替换 |
偏特化 | template<typename T> | 部分占位 | 中等 | 对一类类型做定制 |
3 非特化:万能配方
// 主模板:任何 T 都能实例化
template<typename T>
struct Add {static T apply(T a, T b) { return a + b; }
};
- 占位符:
T
完全开放。 - 实例化示例:
Add<int>::apply
会生成int Add<int>::apply(int a, int b) { return a + b; }
4 全特化:一对一替换
// 全特化:仅对 bool
template<>
struct Add<bool> {static bool apply(bool a, bool b) { return a || b; }
};
- 占位符:空 (
template<>
)。 - 匹配规则:只要实参是 精确类型
bool
,就跳过主模板,直接用这段代码。 - 无继承、无虚函数,只是生成另一份普通函数。
5 偏特化:一对“模式”替换
// 主模板:任意 T
template<typename T>
struct IsPointer { static constexpr bool value = false; };// 偏特化:所有指针类型
template<typename T>
struct IsPointer<T*> { static constexpr bool value = true; };
- 占位符:保留部分参数 (
T*
),其余由实参推导。 - 匹配规则:
IsPointer<int*>::value
⇒ 命中偏特化,值为true
IsPointer<int>::value
⇒ 回退主模板,值为false
偏特化常用于:
- 指针 / 引用 / 数组 / 函数类型 的差异化处理
- STL 的
vector<T*>
特化优化内存布局
6 优先级与决策链(编译期)
编译器按 “精确 → 模式 → 通用” 三级筛选:
给定实参: T = int*
1. 有全特化<int*>? → 否
2. 有偏特化<T*> 匹配? → 是,使用
3. 回退主模板 → 不需要
一旦确定,生成的函数符号就是 静态地址,运行时不再判断。
7 与动态多态的区别
维度 | 模板特化 | 虚函数多态 |
---|---|---|
决策时机 | 编译期 | 运行期 |
机制 | 代码生成 + 符号重载 | vptr + vtable |
额外成本 | 零 | 一次间接调用 |
可扩展性 | 需重编译 | 运行时可加载 |
8 小结
- 非特化:给编译器一张“万能蓝图”。
- 全特化:为某个具体类型提供完全替换的实现。
- 偏特化:为一类类型模式提供定制实现。
- 全部抉择在 编译期完成,运行时与普通函数/类 毫无区别。
背住这张图即可:
源码阶段 → 模板实参 → 匹配优先级 → 实例化 → 静态符号 → 机器码