模板特化

全特化

针对某个特定的模板参数 提供一个完全独立的实现,完全替代 原始的模板定义。

函数模板全特化:

1
2
3
4
5
6
7
8
9
10
11
12
// 通用模板定义
template<typename T, typename U>
void Message(T n1, U n2) {
std::cout << n1 << " " << n2 << std::endl;
}

// 全特化
template<>
void Message<int, double>(int n1, double n2) {
std::cout << "全特化" << std::endl;
std::cout << n1 << " " << n2 << std::endl;
}
复制代码

类模板全特化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 通用模板
template <typename T>
class Printer {
public:
void print() {
std::cout << "通用打印器\n";
}
};

// 针对 int 类型的全特化
template <>
class Printer<int> {
public:
void print() {
std::cout << "专门处理 int 类型的打印器\n";
}
};
复制代码

从全特化可以看出这样几个特点:

  1. 由于全特化是把模板中的参数或者参数类型全部都确定,也就无需在 template<> 中写任何内容,而是在函数名或者类名后面添加确定的参数类型或参数
  2. 全特化就和原始模板没有关系,可以看出全特化的 Message 函数可以实现其他内容
  3. 如果用户在 确定 第一个参数类型是 int,第二个参数类型是 double,那么就会优先走全特化,而不是通用模板定义

你可能有个疑问,如果是非类型参数,全特化是不是也要确定?没错!!!

偏特化

不完全固定所有模板参数 的情况下,为特定情况提供自定义实现。它适用于类模板,但不适用于函数模板(C++ 没有直接支持函数模板的部分特化,但可以用重载+SFINAE等方式模拟)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 通用模板
template <typename T1, typename T2>
class Example {
public:
void show() {
std::cout << "通用模板: T1, T2\n";
}
};

// 偏特化:当 T2 固定为 int 时
template <typename T1>
class Example<T1, int> {
public:
void show() {
std::cout << "偏特化: T2 被固定为 int\n";
}
};
复制代码

C++ 为什么没有直接支持函数模板的部分特化

C++ 通过函数重载来选择最匹配的函数,而类模板的部分特化是通过模式匹配来选择最匹配的模板特化。

函数模板的部分特化会与现有的函数模板重载机制产生歧义,导致编译器无法明确选择合适的匹配方式。

我们可以使用重载、SFINAE(C++11 支持) 和 concepts(C++20 支持) 来实现相同的功能,同时避免编译器的二义性问题。