全特化是完全指定所有模板参数的特化形式,必须以template开头,仅适用于类和函数模板;偏特化仅适用于类模板,需至少保留一个未定参数,不适用于函数模板。

全特化就是“完全指定所有模板参数”
当你为某个具体类型组合(比如 int、std::string 或 MyClass)单独写一份模板实现,那就是全特化。编译器在匹配时,会优先选它,而不是泛型版本。
常见错误是把全特化写成新函数或类声明,漏掉 template 前缀——这会导致链接错误或重定义,因为编译器认不出这是特化。
- 必须以
template开头,后面紧跟类名或函数名+尖括号内具体类型 - 全特化不是重载,不能只改返回类型或参数名;类型必须完全一致
- 类模板全特化可以定义成员函数体;函数模板全特化不能偏移调用语义(比如不能加默认参数)
示例:
template<typename t> struct Box { T val; };
template struct Box<int> { int val = 42; }; // ✅ 全特化
template struct Box<:string> { std::string val; };</:string></int></typename>
偏特化只适用于类模板,且必须“留至少一个模板参数未定”
C++ 不允许函数模板偏特化,这是硬性限制。偏特化的本质是“模式匹配”:你给出的是类型模式,不是具体类型。比如 T*、T&、std::vector 都算合法偏特化形式。
容易踩的坑是试图对基本类型做偏特化(如 template),这其实是全特化;或者写成 template 却忘了偏特化只能用于类模板——函数模板这么写直接报错。
- 偏特化声明必须带
template<...>,且参数列表不能全为空 - 偏特化不能有默认模板参数(否则编译器无法判断是否冲突)
- 多个偏特化之间不能有歧义:比如
T*和const T*可能同时匹配const int*,触发编译错误
匹配顺序决定谁生效:全特化 > 偏特化 > 泛型
这不是优先级设置,而是编译器硬编码的匹配规则。比如你有泛型 template,偏特化 template,全特化 template class A,那么 A 一定走全特化,A 走偏特化,A 走泛型。
性能上没区别,但可读性和维护成本差别很大:偏特化嵌套深了(比如 A<:shared_ptr>&>)会让 SFINAE 推导变慢,Clang 有时会报 instantiation depth exceeded。
- 别为了“看起来更通用”而滥用偏特化;多数场景用
if constexpr+ 泛型更清晰 - 偏特化里不能用
sizeof(T)这类依赖不完整类型的表达式,否则可能在实例化前就失败 - 全特化可以定义静态数据成员,偏特化不行(C++17 起允许 inline static,但需显式定义)
函数模板只有全特化,没有偏特化
这是 C++ 标准明确禁止的。如果你看到有人写了类似 template 的“偏特化”,那其实是函数重载,不是特化。编译器按重载解析规则选,和模板匹配机制无关。
后果很实际:重载不会参与 SFINAE,而特化会;你用 std::is_same_v 测函数签名时,重载版本可能被忽略,导致 trait 判定出错。
- 想实现类似偏特化的行为,用
enable_if+ 泛型函数,或 C++20 概念(requires) - 函数全特化要小心:如果泛型版本有默认参数,全特化版本必须显式提供所有实参,不能省略
- 函数全特化不能在头文件里多次定义(除非 inline),否则 ODR 违反
真正需要区分行为时,类模板偏特化 + 静态成员函数,比一堆函数重载更可控。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!









