偏特化(Partial Specialization)理解
偏特化 是指:当模板有多个参数时,只对部分参数进行特化,而不是全部特化。
核心概念对比
| 特化类型 | 参数情况 | 示例 |
|---|---|---|
| 全特化 | 所有参数都固定 | template<> class X<int, double> |
| 偏特化 | 只固定部分参数 | template<class T> class X<T, int> |
1. 基础示例理解
cpp
// 主模板:通用的情况
template<typename T1, typename T2>
class MyClass {
public:
void print() { cout << "General version" << endl; }
};
// 偏特化1:第二个参数固定为 int
template<typename T1>
class MyClass<T1, int> {
public:
void print() { cout << "Partial: second is int" << endl; }
};
// 偏特化2:两个参数都是指针
template<typename T1, typename T2>
class MyClass<T1*, T2*> {
public:
void print() { cout << "Partial: both pointers" << endl; }
};
// 偏特化3:两个参数相同类型
template<typename T>
class MyClass<T, T> {
public:
void print() { cout << "Partial: same type" << endl; }
};
// 使用示例
MyClass<double, string> obj1; // 使用主模板
MyClass<double, int> obj2; // 使用偏特化1
MyClass<int*, double*> obj3; // 使用偏特化2
MyClass<int, int> obj4; // 使用偏特化3
2. 为什么需要偏特化?
cpp
// 场景:一个存储容器,但对指针类型特殊处理
template<typename T>
class Storage {
T data;
public:
void set(const T& val) { data = val; }
T get() { return data; }
};
// 偏特化:T 为指针类型时,使用不同的实现
template<typename T>
class Storage<T*> { // 注意:这里 T 仍然可以变化
T* data;
public:
void set(T* val) {
data = new T(*val); // 指针需要深拷贝
}
T* get() {
return data;
}
~Storage() { delete data; }
};
// 使用
Storage<int> s1; // 普通类型
s1.set(100); // 直接拷贝
Storage<int> s2; // 指针类型,使用偏特化版本
int* p = new int(200);
s2.set(p); // 会深拷贝
3. 常见偏特化模式
cpp
// 模式1:固定一个类型
template<typename T, typename Alloc>
class MyVector {};
template<typename T>
class MyVector<T, std::allocator<T>> {};
// 模式2:指针偏特化
template<typename T>
class Wrapper {};
template<typename T>
class Wrapper<T*> {};
// 模式3:const 偏特化
template<typename T>
class Processor {};
template<typename T>
class Processor<const T> {};
// 模式4:引用偏特化
template<typename T>
class Handler {};
template<typename T>
class Handler<T&> {};
// 模式5:数组偏特化
template<typename T, int N>
class ArrayWrapper {};
template<typename T>
class ArrayWrapper<T, 0> {}; // 空数组特化
4. 实际应用:智能指针的偏特化
cpp
// 简化的智能指针实现
template<typename T>
class SmartPtr {
T* ptr;
public:
SmartPtr(T* p) : ptr(p) {}
T* operator->() { return ptr; }
T& operator*() { return *ptr; }
};
// 偏特化:针对数组类型
template<typename T>
class SmartPtr<T[]> { // 数组版本
T* ptr;
public:
SmartPtr(T* p) : ptr(p) {}
T& operator[](int index) { return ptr[index]; }
~SmartPtr() { delete[] ptr; }
};
// 使用
SmartPtr<int> p1(new int(5)); // 普通指针
*p1 = 10; // OK
SmartPtr<int[]> p2(new int[10]); // 数组指针
p2[0] = 5; // 支持下标访问
5. 偏特化的限制
cpp
// ❌ 错误:不能偏特化函数模板
template<typename T>
void func(T x) {}
template<typename T> // 错误!函数不能偏特化
void func<T*>(T* x) {}
// ✅ 正确:用重载代替
template<typename T>
void func(T x) {}
template<typename T>
void func(T* x) {} // 函数重载
// ❌ 错误:不能偏特化类模板的成员函数
template<typename T>
class Demo {
void method();
};
// 错误!不能只偏特化成员函数
template<typename T>
void Demo<T*>::method() {}
6. 偏特化 vs 重载
cpp
// 类模板:只能用偏特化
template<typename T>
class Container {};
template<typename T>
class Container<T*> {}; // 偏特化
// 函数模板:用重载,不用偏特化
template<typename T>
void process(T val) {}
template<typename T>
void process(T* val) {} // 重载,不是偏特化
7. 匹配优先级
编译器选择最特化的版本:
cpp
template<typename T> class X {}; // 主模板(通用)
template<typename T> class X<T*> {}; // 偏特化1
template<typename T> class X<const T*> {}; // 偏特化2
X<int> x1; // 主模板
X<int*> x2; // 偏特化1(匹配指针)
X<const int*> x3; // 偏特化2(匹配 const 指针)
理解要点总结
- 偏特化 = 部分特殊化,不是全部固定
- 语法 :
template<剩余参数> class 类名<固定参数, 剩余参数> - 目的:对特定模式进行特殊处理(如指针、const、数组等)
- 只能用于类模板,函数模板用重载代替
- 更特化的版本优先匹配
- 偏特化参数必须是主模板参数的子集
记忆口诀:全特化锁定所有类型,偏特化锁定类型模式,指针、数组、const 都可以偏特化处理。