C++模板教程:特化、偏特化与类型萃取
本文摘要
模板是「通用配方」,但某些特殊类型需要专门的实现。// 通用模板:处理一般类型a : b;// 全特化:为特定类型定制(如 const char* 比较字符串而非指针)a : b;类型萃取(Type…
本文是 C++ 系列教程的第 17 篇。上一篇讲解了函数模板与类模板入门,本篇深入模板进阶:函数模板全特化、类模板全特化与偏特化、<type_traits> 类型萃取(is_integral/is_pointer/decay 等)、自定义 traits。
一、模板特化概述
1.1 什么是特化
模板是「通用配方」,但某些特殊类型需要专门的实现。特化就是为特定类型提供定制版本:
// 通用模板:处理一般类型template <typename T>T myMax(T a, T b) { return a > b ? a : b;}
// 全特化:为特定类型定制(如 const char* 比较字符串而非指针)template <>const char *myMax(const char *a, const char *b) { return strcmp(a, b) > 0 ? a : b;}1.2 特化的种类
| 种类 | 说明 | 示例 |
|---|---|---|
| 函数模板全特化 | 所有参数都确定 | myMax<const char*> |
| 类模板全特化 | 所有参数都确定 | Stack<bool> |
| 类模板偏特化 | 部分参数确定 | Stack<T*> |
二、函数模板特化
2.1 函数模板全特化
#include <iostream>#include <cstring>using namespace std;
// 通用模板template <typename T>T myMax(T a, T b) { cout << "通用版本" << endl; return a > b ? a : b;}
// 全特化:const char* 版本template <>const char *myMax<const char *>(const char *a, const char *b) { cout << "特化版本" << endl; return strcmp(a, b) > 0 ? a : b;}
int main() { cout << myMax(3, 7) << endl; // 通用版本 → 7
const char *s1 = "apple"; const char *s2 = "banana"; cout << myMax(s1, s2) << endl; // 特化版本 → banana return 0;}2.2 用重载代替函数特化
建议:函数模板尽量用重载而非特化(更符合直觉、更安全):
#include <iostream>#include <cstring>using namespace std;
template <typename T>T myMax(T a, T b) { return a > b ? a : b;}
// 用重载实现 const char* 版本const char *myMax(const char *a, const char *b) { return strcmp(a, b) > 0 ? a : b;}
int main() { cout << myMax(3, 7) << endl; // 7 cout << myMax("apple", "banana") << endl; // banana return 0;}三、类模板全特化
3.1 全特化语法
#include <iostream>using namespace std;
// 通用类模板template <typename T>class DataBox {private: T value;
public: DataBox(T v) : value(v) {} void show() const { cout << "通用: " << value << endl; } T get() const { return value; }};
// 全特化:bool 版本(用位压缩节省内存)template <>class DataBox<bool> {private: bool value;
public: DataBox(bool v) : value(v) {} void show() const { cout << "bool 特化: " << (value ? "true" : "false") << endl; } bool get() const { return value; }};
int main() { DataBox<int> db1(42); DataBox<string> db2("Hello"); DataBox<bool> db3(true); // 使用特化版本
db1.show(); // 通用: 42 db2.show(); // 通用: Hello db3.show(); // bool 特化: true return 0;}3.2 全特化的经典用途:vector
标准库中 vector<bool> 就是特化的典型例子——用位压缩存储,节省 8 倍内存(但代价是返回的是代理对象)。
四、类模板偏特化
4.1 指针偏特化
#include <iostream>using namespace std;
// 通用模板template <typename T>class Handler {public: static void process(const T &value) { cout << "通用处理: " << value << endl; }};
// 偏特化:指针类型template <typename T>class Handler<T *> {public: static void process(const T *ptr) { cout << "指针处理: " << *ptr << "(地址 " << ptr << ")" << endl; }};
int main() { Handler<int> h1; h1.process(42); // 通用处理: 42
int value = 99; Handler<int *> h2; // 偏特化 h2.process(&value); // 指针处理: 99(地址 0x...) return 0;}4.2 多参数的偏特化
#include <iostream>using namespace std;
// 通用:两个类型template <typename T1, typename T2>class Printer {public: static void print(const T1 &a, const T2 &b) { cout << "通用: " << a << ", " << b << endl; }};
// 偏特化:两个参数类型相同template <typename T>class Printer<T, T> {public: static void print(const T &a, const T &b) { cout << "同类型特化: " << a << " 与 " << b << endl; }};
// 偏特化:第一个参数是指针template <typename T, typename T2>class Printer<T *, T2> {public: static void print(T *ptr, const T2 &b) { cout << "指针特化: " << *ptr << ", " << b << endl; }};
int main() { Printer<int, double> p1; p1.print(3, 4.5); // 通用: 3, 4.5
Printer<int, int> p2; p2.print(3, 4); // 同类型特化: 3 与 4
int x = 10; Printer<int *, double> p3; p3.print(&x, 2.5); // 指针特化: 10, 2.5 return 0;}4.3 偏特化的选择规则
- 全特化优先于偏特化。
- 偏特化优先于通用模板。
- 更具体的偏特化优先(如
T*比T更具体)。
五、类型萃取 type_traits
5.1 什么是类型萃取
类型萃取(Type Traits)是在编译期查询和操作类型属性的技术。C++11 起标准库提供 <type_traits>:
#include <iostream>#include <type_traits>using namespace std;
int main() { // 类型属性查询(编译期常量) cout << "int 是整数: " << is_integral<int>::value << endl; // 1 cout << "double 是整数: " << is_integral<double>::value << endl; // 0 cout << "int* 是指针: " << is_pointer<int *>::value << endl; // 1 cout << "int 是指针: " << is_pointer<int>::value << endl; // 0
// 类型关系 cout << "int == signed int: " << is_same<int, signed int>::value << endl; // 1 cout << "in'B��V�6�v�VC�"���5�6�SƖ�B�V�6�v�VC㣧f�VR��V�Fò�������2��rz�X�n�ɥ�bY�{��6�WB��&F�V&�Ri��kZ�x+��"���5�f��F��u����E�c�F�V&�S���V�Fò���&WGW&���Ц��222R�"[��yJ�{;�Y辉>X�`����>X�b�Y�K���zK�K�������������������5���FVw&��C��i��Yni[NY����B����r�6�"����5�f��F��u����C�C��i��YnkZ�x+��f��B�F�V&�R����5����FW#�C��i��Ynh�~�(����B��f��B�����5�6�73�C��i��Yn{�{�Y��6�72�7G'V7B����5�6�S�B�S��K�N{�Y�i��Yny��Y������5�6��7C�C��i��Yb�v��7B����&V��fU�6��7C�C��X뾙�@ const | const int → int || `remove_reference<T>` | 去除引用 | int& → int || `decay<T>` | 退化类型 | const int& → int |
### 5.3 decay 与 remove_reference
```cpp#include <iostream>#include <type_traits>using namespace std;
int main() { // remove_reference:去掉引用 using T1 = remove_reference<int &>::type; cout << "remove_reference<int&> == int: " << is_same<T1, int>::value << endl; // 1
// remove_const:去掉 const using T2 = remove_const<const int>::type; cout << "remove_const<const int> == int: " << is_same<T2, int>::value << endl; // 1
// decay:数组退化为指针,const& 退化为值 using T3 = decay<const int &>::type; cout << "decay<const int&> == int: " << is_same<T3, int>::value << endl; // 1
using T4 = decay<int[5]>::type; cout << "decay<int[5]> == int*: " << is_same<T4, int *>::value << endl; // 1 return 0;}六、自定义类型萃取
6.1 自定义 trait 类
#include <iostream>using namespace std;
// 自定义 trait:判断是否为容器类型template <typename T>struct IsContainer { static const bool value = false;};
// 特化:数组是容器template <typename T, size_t N>struct IsContainer<T[N]> { static const bool value = true;};
// C++11 风格:使用 integral_constanttemplate <typename T>struct IsPointer { static const bool value = false;};
template <typename T>struct IsPointer<T *> { static const bool value = true;};
int main() { cout << "int 是容器: " << IsContainer<int>::value << endl; // 0 cout << "int[5] 是容器: " << IsContainer<int[5]>::value << endl; // 1 cout << "int* 是指针: " << IsPointer<int *>::value << endl; // 1 cout << "int 是指针: " << IsPointer<int>::value << endl; // 0 return 0;}6.2 基于萃取的类型分派
#include <iostream>#include <type_traits>using namespace std;
// 整数版本void printImpl(int value, true_type) { cout << "整数: " << value << endl;}
// 其他类型版本void printImpl(const auto &value, false_type) { cout << "其他: " << value << endl;}
// 用萃取在编译期分派template <typename T>void print(const T &value) { // is_integral<T>::type 是 true_type 或 false_type printImpl(value, typename is_integral<T>::type());}
int main() { print(42); // 整数: 42 print(3.14); // 其他: 3.14 print("hello"); // 其他: hello return 0;}6.3 自定义 IsSame trait
#include <iostream>using namespace std;
// 通用:不相同template <typename T, typename U>struct IsSame { static const bool value = false;};
// 特化:相同时template <typename T>struct IsSame<T, T> { static const bool value = true;};
// 便捷变量模板(C++17)template <typename T, typename U>const bool IsSame_v = IsSame<T, U>::value;
int main() { cout << IsSame<int, int>::value << endl; // 1 cout << IsSame<int, double>::value << endl; // 0 cout << IsSame_v<int, int> << endl; // 1(C++17) return 0;}七、实战:通用打印工具
综合本篇知识,实现按类型优化的打印:
#include <iostream>#include <type_traits>#include <vector>using namespace std;
// 打印工具类template <typename T>class TypePrinter {public: // 编译期判断类型并打印 static void print(const T &value) { printImpl(value, typename is_integral<T>::type(), typename is_floating_point<T>::type()); }
private: // 整数 static void printImpl(const T &v, true_type, false_type) { cout << "[整数] " << v << endl; } // 浮点 static void printImpl(const T &v, false_type, true_type) { cout << "[浮点] " << v << endl; } // 其他 static void printImpl(const T &v, false_type, false_type) { cout << "[其他] " << v << endl; }};
// vector 特化打印template <typename T>void printVector(const vector<T> &vec) { cout << "[vector<" << typeid(T).name() << "> 大小 " << vec.size() << "] "; for (const auto &item : vec) cout << item << " "; cout << endl;}
int main() { TypePrinter<int>::print(42); // [整数] 42 TypePrinter<double>::print(3.14); // [浮点] 3.14 TypePrinter<string>::print("hi"); // [其他] hi
vector<int> nums = {1, 2, 3}; printVector(nums);
vector<string> words = {"C++", "Java"}; printVector(words); return 0;}总结
本篇讲解了模板特化的概念与种类、函数模板全特化(及用重载替代)、类模板全特化与偏特化的语法和选择规则、type_traits 类型萃取(is_* 系列、remove_* 系列、decay)、自定义 trait 类与类型分派,并用通用打印工具串联实战。重点掌握:全特化 vs 偏特化、_v 后缀简化写法、is_same/decay 的使用、基于萃取的编译期分派。
下一篇将讲解变参模板、折叠表达式与 SFINAE,敬请期待!
C++模板教程:特化、偏特化与类型萃取
作者:Wang Shijie
本文链接:https://wsj-blog.pages.dev/posts/c-mu-ban-jiao-cheng-te-hua-pian-te-hua-yu-lei-xing-cui-qu/
本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
留言评论
评论资源加载失败,请重试。