【c++面向对象编程】第42篇:模板特化与偏特化:为特定类型定制实现 目录一、一个需要特化的场景二、函数模板的全特化语法注意事项三、类模板的全特化语法全特化后可以完全不同四、偏特化Partial Specialization1. 指针类型的偏特化2. const 类型的偏特化3. 引用类型的偏特化4. 多个模板参数的偏特化五、数组类型的特化六、模板元编程启蒙编译期阶乘编译期类型判断C11 起有更便捷的标准库七、完整例子类型萃取与智能打印八、常见错误1. 函数模板偏特化不存在2. 特化放在泛型版本之前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 printstring(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* maxconst 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; }三、类模板的全特化语法cpptemplate typename T class Storage { public: void describe() { cout 通用存储 endl; } }; // 全特化为 bool 定制 template class Storagebool { public: void describe() { cout 布尔值存储可能用位 endl; } }; int main() { Storageint s1; s1.describe(); // 通用存储 Storagebool s2; s2.describe(); // 布尔值存储 }全特化后可以完全不同cpptemplate class Storagevoid { // 可以完全重新设计成员不遵循原模板的结构 };四、偏特化Partial Specialization偏特化为一组类型提供定制而不是单个具体类型。1. 指针类型的偏特化cpptemplate typename T class Wrapper { public: void print() { cout 普通类型 endl; } }; // 偏特化所有指针类型 template typename T class WrapperT* { public: void print() { cout 指针类型指向: typeid(T).name() endl; } }; int main() { Wrapperint w1; w1.print(); // 普通类型 Wrapperint* w2; w2.print(); // 指针类型指向: int }2. const 类型的偏特化cpptemplate typename T class Processor { public: void process() { cout 普通版本 endl; } }; template typename T class Processorconst T { public: void process() { cout const 版本 endl; } }; // 使用 Processorint p1; // 普通版本 Processorconst int p2; // const 版本3. 引用类型的偏特化cpptemplate typename T class Handler { public: void handle() { cout 传值 endl; } }; template typename T class HandlerT { public: void handle() { cout 左值引用 endl; } }; template typename T class HandlerT { public: void handle() { cout 右值引用 endl; } };4. 多个模板参数的偏特化cpptemplate typename K, typename V class Map { // 泛型版本 }; // 偏特化第二个参数是指针 template typename K, typename V class MapK, V* { // 特殊处理指针类型的 value }; // 偏特化两个类型相同 template typename T class MapT, 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 * FactorialN - 1::value; }; // 特化终止条件 template struct Factorial0 { static constexpr int value 1; }; int main() { // 编译期计算运行时只是读取常量 cout Factorial5::value endl; // 120 // 等价于 cout 120 endl; }编译期类型判断cpp// 主模板假设不是指针 template typename T struct IsPointer { static constexpr bool value false; }; // 偏特化匹配指针类型 template typename T struct IsPointerT* { static constexpr bool value true; }; int main() { cout IsPointerint::value endl; // 0 cout IsPointerint*::value endl; // 1 cout IsPointerconst char*::value endl; // 1 }C11 起有更便捷的标准库cpp#include type_traits cout is_pointerint::value endl; // 0 cout is_pointerint*::value endl; // 1 cout is_constconst int::value endl; // 1 cout is_sameint, 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 HasPrintint { static constexpr bool value true; }; template struct HasPrintdouble { static constexpr bool value true; }; template struct HasPrintstring { 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 Printerint { public: static void print(const int value) { cout int: value endl; } }; // 特化double template class Printerdouble { public: static void print(const double value) { cout double: value endl; } }; // 特化string template class Printerstring { public: static void print(const string value) { cout string: \ value \ endl; } }; // 偏特化所有指针类型 template typename T class PrinterT* { public: static void print(const T* value) { if (value) { cout 指针 value 指向: ; PrinterT::print(*value); } else { cout 空指针 endl; } } }; // 偏特化vector template typename T class PrintervectorT { public: static void print(const vectorT vec) { cout vector[ vec.size() ]: { ; for (size_t i 0; i vec.size() i 5; i) { PrinterT::print(vec[i]); if (i vec.size() - 1) cout , ; } if (vec.size() 5) cout ...; cout } endl; } }; // 辅助函数 template typename T void smartPrint(const T value) { PrinterT::print(value); } int main() { smartPrint(42); smartPrint(3.14159); smartPrint(string(Hello C)); int x 100; smartPrint(x); vectorint vec {1, 2, 3, 4, 5, 6, 7}; smartPrint(vec); smartPrint(nullptr); // 特殊处理空指针 return 0; }输出textint: 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 funcT*(T* t) { } // 编译错误解决方案用函数重载或类模板偏特化。2. 特化放在泛型版本之前cpp// ❌ 错误特化必须在泛型版本之后 template void funcint(int x) { } template typename T void func(T x) { }3. 忘记处理 const 和 volatilecpp// 特化处理指针但 const int* 不匹配 int* template typename T class WrapperT* { }; Wrapperconst int* w; // 匹配的是泛型版本不是指针特化 // 需要额外提供 const T* 的偏特化九、这一篇的收获你现在应该理解全特化为具体类型如int、string提供定制实现偏特化为一类类型指针、引用、const、数组提供定制函数模板全特化存在但不推荐优先用重载类模板全特化/偏特化常用于类型萃取和编译期计算模板元编程启蒙特化 递归 编译期计算 小作业实现一个IsSame类型萃取编译期判断两个类型是否相同不能用std::is_same。实现一个RemovePointer去掉类型的指针层数如int***→int。下一篇预告第43篇《可变参数模板C11优雅处理不定长参数》——...语法、递归展开、sizeof...运算符、tuple的实现原理。可变参数模板是 C11 的重要特性让模板可以接受任意数量的参数。