目录
[四、偏特化(Partial Specialization)](#四、偏特化(Partial Specialization))
[1. 指针类型的偏特化](#1. 指针类型的偏特化)
[2. const 类型的偏特化](#2. const 类型的偏特化)
[3. 引用类型的偏特化](#3. 引用类型的偏特化)
[4. 多个模板参数的偏特化](#4. 多个模板参数的偏特化)
[C++11 起有更便捷的标准库](#C++11 起有更便捷的标准库)
[1. 函数模板偏特化不存在](#1. 函数模板偏特化不存在)
[2. 特化放在泛型版本之前](#2. 特化放在泛型版本之前)
[3. 忘记处理 const 和 volatile](#3. 忘记处理 const 和 volatile)
一、一个需要特化的场景
写一个 print 函数,对大多数类型直接输出,但对 string 加引号:
cpp
#include <iostream>
#include <string>
using namespace std;
// 泛型版本
template <typename T>
void print(const T& value) {
cout << value << endl;
}
// 特化版本:为 string 定制
template <>
void print<string>(const string& value) {
cout << "\"" << value << "\"" << endl;
}
int main() {
print(42); // 输出: 42
print(3.14); // 输出: 3.14
print("hello"); // 注意:这里 T 是 char[6],不是 string
string s = "world";
print(s); // 输出: "world"
}
注意 :print("hello") 不会触发 string 特化,因为 "hello" 是 const char[6],不是 std::string。
二、函数模板的全特化
语法
cpp
// 1. 泛型版本
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
// 2. 全特化:为 const char* 定制
template <>
const char* max<const char*>(const char* a, const char* b) {
return strcmp(a, b) > 0 ? a : b;
}
// 或者省略 <const char*>,编译器可推导
template <>
const char* max(const char* a, const char* b) {
return strcmp(a, b) > 0 ? a : b;
}
注意事项
-
全特化不能单独出现,必须有泛型版本
-
函数特化不参与重载,只影响模板实例化时的选择
-
通常函数重载比重载更清晰,推荐用重载代替特化
cpp
// 推荐:直接用普通函数重载
void print(const string& value) {
cout << "\"" << value << "\"" << endl;
}
三、类模板的全特化
语法
cpp
template <typename T>
class Storage {
public:
void describe() {
cout << "通用存储" << endl;
}
};
// 全特化:为 bool 定制
template <>
class Storage<bool> {
public:
void describe() {
cout << "布尔值存储(可能用位)" << endl;
}
};
int main() {
Storage<int> s1;
s1.describe(); // 通用存储
Storage<bool> s2;
s2.describe(); // 布尔值存储
}
全特化后可以完全不同
cpp
template <>
class Storage<void> {
// 可以完全重新设计成员,不遵循原模板的结构
};
四、偏特化(Partial Specialization)
偏特化为一组类型提供定制,而不是单个具体类型。
1. 指针类型的偏特化
cpp
template <typename T>
class Wrapper {
public:
void print() {
cout << "普通类型" << endl;
}
};
// 偏特化:所有指针类型
template <typename T>
class Wrapper<T*> {
public:
void print() {
cout << "指针类型,指向: " << typeid(T).name() << endl;
}
};
int main() {
Wrapper<int> w1;
w1.print(); // 普通类型
Wrapper<int*> w2;
w2.print(); // 指针类型,指向: int
}
2. const 类型的偏特化
cpp
template <typename T>
class Processor {
public:
void process() { cout << "普通版本" << endl; }
};
template <typename T>
class Processor<const T> {
public:
void process() { cout << "const 版本" << endl; }
};
// 使用
Processor<int> p1; // 普通版本
Processor<const int> p2; // const 版本
3. 引用类型的偏特化
cpp
template <typename T>
class Handler {
public:
void handle() { cout << "传值" << endl; }
};
template <typename T>
class Handler<T&> {
public:
void handle() { cout << "左值引用" << endl; }
};
template <typename T>
class Handler<T&&> {
public:
void handle() { cout << "右值引用" << endl; }
};
4. 多个模板参数的偏特化
cpp
template <typename K, typename V>
class Map {
// 泛型版本
};
// 偏特化:第二个参数是指针
template <typename K, typename V>
class Map<K, V*> {
// 特殊处理指针类型的 value
};
// 偏特化:两个类型相同
template <typename T>
class Map<T, T> {
// 键值类型相同时的优化
};
五、数组类型的特化
cpp
#include <iostream>
using namespace std;
// 泛型版本:获取数组大小(失败)
template <typename T>
void arraySize(T&) {
cout << "不是数组类型" << endl;
}
// 偏特化:捕获数组长度
template <typename T, size_t N>
void arraySize(T (&)[N]) {
cout << "数组长度: " << N << endl;
}
int main() {
int arr[10];
arraySize(arr); // 数组长度: 10
int* p = arr;
arraySize(p); // 不是数组类型
}
这是 STL 中 std::extent、std::rank 等类型萃取的基础。
六、模板元编程启蒙
模板特化是编译期计算的基石------模板元编程(Template Metaprogramming, TMP)。
编译期阶乘
cpp
// 泛型版本(递归)
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
// 特化:终止条件
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
int main() {
// 编译期计算,运行时只是读取常量
cout << Factorial<5>::value << endl; // 120
// 等价于 cout << 120 << endl;
}
编译期类型判断
cpp
// 主模板:假设不是指针
template <typename T>
struct IsPointer {
static constexpr bool value = false;
};
// 偏特化:匹配指针类型
template <typename T>
struct IsPointer<T*> {
static constexpr bool value = true;
};
int main() {
cout << IsPointer<int>::value << endl; // 0
cout << IsPointer<int*>::value << endl; // 1
cout << IsPointer<const char*>::value << endl; // 1
}
C++11 起有更便捷的标准库
cpp
#include <type_traits>
cout << is_pointer<int>::value << endl; // 0
cout << is_pointer<int*>::value << endl; // 1
cout << is_const<const int>::value << endl; // 1
cout << is_same<int, long>::value << endl; // 0
七、完整例子:类型萃取与智能打印
cpp
#include <iostream>
#include <vector>
#include <string>
#include <typeinfo>
using namespace std;
// ========== 类型萃取:判断是否可打印 ==========
template <typename T>
struct HasPrint {
static constexpr bool value = false;
};
template <>
struct HasPrint<int> {
static constexpr bool value = true;
};
template <>
struct HasPrint<double> {
static constexpr bool value = true;
};
template <>
struct HasPrint<string> {
static constexpr bool value = true;
};
// ========== 模板特化实现 Printer ==========
// 泛型版本:不可打印的类型
template <typename T>
class Printer {
public:
static void print(const T& value) {
cout << "[不可打印类型: " << typeid(T).name() << "]" << endl;
}
};
// 特化:int
template <>
class Printer<int> {
public:
static void print(const int& value) {
cout << "int: " << value << endl;
}
};
// 特化:double
template <>
class Printer<double> {
public:
static void print(const double& value) {
cout << "double: " << value << endl;
}
};
// 特化:string
template <>
class Printer<string> {
public:
static void print(const string& value) {
cout << "string: \"" << value << "\"" << endl;
}
};
// 偏特化:所有指针类型
template <typename T>
class Printer<T*> {
public:
static void print(const T* value) {
if (value) {
cout << "指针 @" << value << " 指向: ";
Printer<T>::print(*value);
} else {
cout << "空指针" << endl;
}
}
};
// 偏特化:vector
template <typename T>
class Printer<vector<T>> {
public:
static void print(const vector<T>& vec) {
cout << "vector[" << vec.size() << "]: { ";
for (size_t i = 0; i < vec.size() && i < 5; i++) {
Printer<T>::print(vec[i]);
if (i < vec.size() - 1) cout << ", ";
}
if (vec.size() > 5) cout << "...";
cout << " }" << endl;
}
};
// 辅助函数
template <typename T>
void smartPrint(const T& value) {
Printer<T>::print(value);
}
int main() {
smartPrint(42);
smartPrint(3.14159);
smartPrint(string("Hello C++"));
int x = 100;
smartPrint(&x);
vector<int> vec = {1, 2, 3, 4, 5, 6, 7};
smartPrint(vec);
smartPrint(nullptr); // 特殊处理空指针
return 0;
}
输出:
text
int: 42
double: 3.14159
string: "Hello C++"
指针 @0x7ffee2a8 指向: int: 100
vector[7]: { int: 1, int: 2, int: 3, int: 4, int: 5, ... }
空指针
八、常见错误
1. 函数模板偏特化不存在
cpp
// ❌ 错误:函数模板不支持偏特化
template <typename T>
void func(T t) { }
template <typename T>
void func<T*>(T* t) { } // 编译错误
解决方案:用函数重载或类模板偏特化。
2. 特化放在泛型版本之前
cpp
// ❌ 错误:特化必须在泛型版本之后
template <>
void func<int>(int x) { }
template <typename T>
void func(T x) { }
3. 忘记处理 const 和 volatile
cpp
// 特化处理指针,但 const int* 不匹配 int*
template <typename T>
class Wrapper<T*> { };
Wrapper<const int*> w; // 匹配的是泛型版本,不是指针特化
// 需要额外提供 const T* 的偏特化
九、这一篇的收获
你现在应该理解:
-
全特化 :为具体类型(如
int、string)提供定制实现 -
偏特化 :为一类类型(指针、引用、
const、数组)提供定制 -
函数模板全特化:存在但不推荐,优先用重载
-
类模板全特化/偏特化:常用于类型萃取和编译期计算
-
模板元编程启蒙:特化 + 递归 = 编译期计算
💡 小作业:实现一个
IsSame类型萃取,编译期判断两个类型是否相同(不能用std::is_same)。实现一个RemovePointer,去掉类型的指针层数(如int***→int)。
下一篇预告 :第43篇《可变参数模板(C++11):优雅处理不定长参数》------... 语法、递归展开、sizeof... 运算符、tuple 的实现原理。可变参数模板是 C++11 的重要特性,让模板可以接受任意数量的参数。