C++模板教程:特化、偏特化与类型萃取

🕒 阅读时间:16 分钟📝 字数:2521👀 阅读量:Loading...

本文是 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 偏特化的选择规则

  1. 全特化优先于偏特化。
  2. 偏特化优先于通用模板。
  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��Y
ni[NY����B����r�6�"����5�f��F��u����C�C��i��Y
nkZ�x+��f��B�F�V&�R����5����FW#�C��i��Y
nh�~�(����B��f��B�����5�6�73�C��i��Y
n{�{�Y��6�72�7G'V7B����5�6�S�B�S��K�N{�Y�i��Y
ny��Y������5�6��7C�C��i��Y
b�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_constant
template <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<i
nt, 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,敬请期待!

Creative Commons

留言评论