模板特化与偏特化
定义与作用
当通用模板对某些特定类型需要不同实现时(如 vector<bool> 需要位压缩、指针类型需要解引用比较),使用特化覆盖通用版本:
- 全特化:为某组具体模板实参提供完全不同的实现
- 偏特化(仅类模板):为满足某个模式的一组实参提供实现
template <typename T> // 通用模板
class MyClass { ... };
template <> // 全特化:T = int 时使用
class MyClass<int> { ... };
template <typename T> // 偏特化:T 是指针时使用
class MyClass<T*> { ... };
核心原理
特化选择规则
函数模板不支持偏特化
完整示例
示例一:智能指针的泛型与指针特化
场景说明:设计一个通用比较器类模板,并为指针类型提供解引用比较的偏特化。
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
// ====== 通用比较器模板 ======
template <typename T>
class Comparator {
public:
static bool equal(const T& a, const T& b) {
return a == b;
}
static bool less(const T& a, const T& b) {
return a < b;
}
static int compare(const T& a, const T& b) {
if (less(a, b)) return -1;
if (less(b, a)) return 1;
return 0;
}
};
// ====== 偏特化:指针类型——解引用后比较 ======
template <typename T>
class Comparator<T*> {
public:
static bool equal(const T* a, const T* b) {
if (a == nullptr || b == nullptr)
return a == b;
return Comparator<T>::equal(*a, *b);
}
static bool less(const T* a, const T* b) {
if (a == nullptr) return true;
if (b == nullptr) return false;
return Comparator<T>::less(*a, *b);
}
static int compare(const T* a, const T* b) {
if (a == nullptr && b == nullptr) return 0;
if (a == nullptr) return -1;
if (b == nullptr) return 1;
return Comparator<T>::compare(*a, *b);
}
};
// ====== 全特化:const char* —— C 字符串专用 ======
template <>
class Comparator<const char*> {
public:
static bool equal(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
static bool less(const char* a, const char* b) {
return std::strcmp(a, b) < 0;
}
static int compare(const char* a, const char* b) {
int r = std::strcmp(a, b);
return (r > 0) - (r < 0);
}
};
// 辅助打印函数
template <typename T>
void compare_and_print(const T& a, const T& b, const std::string& tag) {
std::cout << tag << ": ";
if (Comparator<T>::equal(a, b))
std::cout << "相等";
else if (Comparator<T>::less(a, b))
std::cout << a << " < " << b;
else
std::cout << a << " > " << b;
std::cout << std::endl;
}
int main() {
std::cout << "===== 通用类型比较 =====" << std::endl;
compare_and_print(10, 20, "int");
compare_and_print(3.14, 3.14, "double");
compare_and_print(std::string("abc"), std::string("abd"), "string");
std::cout << "\n===== 指针类型比较(偏特化:解引用后比较)=====" << std::endl;
int x = 10, y = 20;
compare_and_print(&x, &y, "int*");
std::string s1 = "hello", s2 = "hello";
compare_and_print(&s1, &s2, "string*");
int* null_ptr = nullptr;
compare_and_print(null_ptr, &x, "int* (null vs &x)");
std::cout << "\n===== C 字符串全特化 =====" << std::endl;
const char* c1 = "apple";
const char* c2 = "banana";
compare_and_print(c1, c2, "const char*");
// 如果不用全特化,会将指针地址比较——显然不对
return 0;
}
预期输出:
===== 通用类型比较 =====
int: 10 < 20
double: 相等
string: abc < abd
===== 指针类型比较(偏特化:解引用后比较)=====
int*: 10 < 20
string*: 相等
int* (null vs &x): nullptr < &x
===== C 字符串全特化 =====
const char*: apple < banana
逐段分析:
- 通用模板
Comparator<T>使用==和<运算符——适用于内置类型和有operator<的自定义类型 - 偏特化
Comparator<T*>匹配所有指针类型——先处理 nullptr 边界,再解引用递归调用Comparator<T> - 全特化
Comparator<const char*>使用strcmp——C 字符串不能用==比较(那比的是地址) - 选择优先级:全特化 > 最特化的偏特化 > 通用模板
示例二:类型信息的编译期提取(traits 模板)
#include <iostream>
#include <string>
// ====== 通用 TypeInfo ======
template <typename T>
struct TypeInfo {
static const char* name() { return "未知类型"; }
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
};
// ====== 偏特化:指针 ======
template <typename T>
struct TypeInfo<T*> {
static std::string name() {
return std::string(TypeInfo<T>::name()) + "*";
}
static constexpr bool is_pointer = true;
static constexpr bool is_reference = false;
};
// ====== 偏特化:引用 ======
template <typename T>
struct TypeInfo<T&> {
static std::string name() {
return std::string(TypeInfo<T>::name()) + "&";
}
static constexpr bool is_pointer = false;
static constexpr bool is_reference = true;
};
// ====== 偏特化:const ======
template <typename T>
struct TypeInfo<const T> {
static std::string name() {
return "const " + std::string(TypeInfo<T>::name());
}
// 继承 T 的 pointer/reference 信息
static constexpr bool is_pointer = TypeInfo<T>::is_pointer;
static constexpr bool is_reference = TypeInfo<T>::is_reference;
};
// ====== 全特化:常见类型 ======
#define REGISTER_TYPE(TYPE, NAME) \
template <> struct TypeInfo<TYPE> { \
static const char* name() { return NAME; } \
static constexpr bool is_pointer = false; \
static constexpr bool is_reference = false; \
}
REGISTER_TYPE(int, "int");
REGISTER_TYPE(double, "double");
REGISTER_TYPE(char, "char");
template <>
struct TypeInfo<std::string> {
static const char* name() { return "std::string"; }
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
};
int main() {
std::cout << "===== 编译期类型信息 =====" << std::endl;
std::cout << "int: " << TypeInfo<int>::name() << std::endl;
std::cout << "int*: " << TypeInfo<int*>::name()
<< " (is_pointer=" << TypeInfo<int*>::is_pointer << ")" << std::endl;
std::cout << "int**: " << TypeInfo<int**>::name() << std::endl;
std::cout << "const int*: " << TypeInfo<const int*>::name() << std::endl;
std::cout << "double&: " << TypeInfo<double&>::name()
<< " (is_reference=" << TypeInfo<double&>::is_reference << ")" << std::endl;
std::cout << "std::string: " << TypeInfo<std::string>::name() << std::endl;
std::cout << "const std::string&: " << TypeInfo<const std::string&>::name() << std::endl;
return 0;
}
预期输出:
===== 编译期类型信息 =====
int: int
int*: int* (is_pointer=true)
int**: int**
const int*: const int*
double&: double& (is_reference=true)
std::string: std::string
const std::string&: const std::string&
逐段分析:
- 偏特化
TypeInfo<T*>层层剥离指针,int**→int** - 偏特化
TypeInfo<const T>剥离 const 后委托给TypeInfo<T>,再附加 "const" 前缀 - 全特化为具体类型提供实际名称——宏
REGISTER_TYPE减少重复代码 - 这是
std::is_pointer、std::is_reference等标准 type_traits 的实现原理
易错场景与面试考点
易错场景
| 场景 | 错误表现 | 正确做法 |
|---|---|---|
| 函数模板使用特化语法 | 编译错误 | 函数模板用重载代替偏特化 |
| 偏特化未覆盖所有使用场景 | 使用通用版本(可能错误) | 检查特化的覆盖范围 |
| 多个偏特化同时匹配 | 歧义错误 | 确保特化层次清晰不重叠 |
| 全特化放在头文件但未加 inline | 多重定义链接错误 | 全特化函数模板加 inline |
| 偏特化中重复定义通用模板已定义的成员 | 代码冗余 | 偏特化只覆盖需要差异化的部分 |
常见面试问题
全特化和偏特化的区别?——全特化为具体的模板实参组合提供实现(
template<> class C<int>{})。偏特化为一类模式(如所有指针T*)提供实现,仅类模板支持。函数模板为什么不支持偏特化?如何替代?——标准规定函数模板只能用全特化。替代方案:(1) 函数重载;(2)
enable_if+ SFINAE;(3) 委托给支持偏特化的类模板。偏特化的匹配规则是什么?——选择"最特化"的版本。
C<T*>比C<T>更特化,C<int*>(全特化)比C<T*>(偏特化)更特化。如果多个偏特化同等程度匹配,产生歧义错误。全特化和显式实例化有什么区别?——全特化:
template<> class C<int> { 自定义实现 };。显式实例化:template class C<int>;(使用通用模板生成,不自定义)。traits 模板如何使用偏特化?——通用 traits 模板定义默认行为,通过偏特化为指针/引用/const 等修饰类型提取不同属性。这是
std::type_traits的基础。
小结
- 全特化为具体类型提供定制实现——
template<> class C<int>{} - 偏特化为一类模式提供实现——仅类模板支持,函数模板用重载替代
- 选择规则:全特化 > 最特化的偏特化 > 通用模板
- traits 模板利用偏特化提取编译期类型信息(如
is_pointer、is_const) - 函数模板的"偏特化":用类模板偏特化 + 静态函数间接实现