C++ 泛型编程

Channing Hsu

在 C++ 中,模板是泛型编程的核心特性,它允许编写与数据类型无关的代码。模板分为类模板函数模板,而模板特化又分为全特化(全特例化)和偏特化(部分特例化)。特化模板是为了对特定类型进行优化,利用特定类型的特性来提高效率。

1. 函数模板的全特化

函数模板的全特化是指对特定类型的参数,提供专门的实现。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

// 主模板
template<typename T1, typename T2>
void fun(T1 a, T2 b) {
cout << "模板函数" << endl;
}

// 全特化
template<>
void fun<int, char>(int a, char b) {
cout << "全特化" << endl;
}

int main() {
fun(10, 'a'); // 调用全特化版本
fun(10, 20); // 调用主模板版本
return 0;
}

说明

  • 主模板 fun(T1 a, T2 b) 适用于所有类型。
  • 全特化版本 fun<int, char>(int a, char b) 仅适用于 intchar 类型。
  • 优先级:调用全特化版本 > 调用主模板版本。

2. 类模板的全特化和偏特化

类模板可以进行全特化和偏特化。全特化是对所有模板参数都进行明确的特定化,而偏特化是对部分模板参数进行特定化。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
using namespace std;

// 主模板
template<typename T1, typename T2>
class Test {
public:
Test(T1 i, T2 j): a(i), b(j) {
cout << "模板类" << endl;
}
private:
T1 a;
T2 b;
};

// 全特化
template<>
class Test<int, char> {
public:
Test(int i, char j): a(i), b(j) {
cout << "全特化" << endl;
}
private:
int a;
char b;
};

// 偏特化
template<typename T2>
class Test<char, T2> {
public:
Test(char i, T2 j): a(i), b(j) {
cout << "偏特化" << endl;
}
private:
char a;
T2 b;
};

int main() {
Test<int, char> t1(10, 'a'); // 调用全特化版本
Test<char, double> t2('b', 3.14); // 调用偏特化版本
Test<int, double> t3(10, 3.14); // 调用主模板版本
return 0;
}

说明

  • 主模板 Test<T1, T2> 适用于所有类型。
  • 全特化 Test<int, char> 仅适用于 intchar 类型。
  • 偏特化 Test<char, T2> 适用于 char 和任何类型 T2 的组合。

3. 优先级

当调用模板时,编译器会根据模板参数匹配情况选择最合适的模板,优先级如下:

  1. 全特化类 > 偏特化类 > 主版本模板类

4. 详细解释

全特化(全特例化)

全特化是指对所有模板参数都进行明确特定的实现。例如,在函数模板中对 fun<int, char> 进行特化意味着这个版本的 fun 函数只适用于 intchar 类型参数。

偏特化(部分特例化)

偏特化是指对部分模板参数进行特定化,而其他参数仍保持泛型。例如,在类模板中对 Test<char, T2> 进行偏特化意味着这个版本的 Test 类适用于第一个参数为 char,而第二个参数可以是任何类型。

使用场景

  • 全特化: 当特定类型组合需要一个完全不同的实现时。
  • 偏特化: 当部分类型参数需要不同的实现,而其他参数仍保持泛型时。

5. 示例代码解析

函数模板示例解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

template<typename T1, typename T2>
void fun(T1 a, T2 b) {
cout << "模板函数" << endl;
}

template<>
void fun<int, char>(int a, char b) {
cout << "全特化" << endl;
}

int main() {
fun(10, 'a'); // 输出:全特化
fun(10, 20); // 输出:模板函数
return 0;
}

在这段代码中:

  • fun(10, 'a') 调用了全特化版本,因为模板参数是 intchar
  • fun(10, 20) 调用了主模板版本,因为模板参数是 intint

类模板示例解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
using namespace std;

template<typename T1, typename T2>
class Test {
public:
Test(T1 i, T2 j): a(i), b(j) {
cout << "模板类" << endl;
}
private:
T1 a;
T2 b;
};

template<>
class Test<int, char> {
public:
Test(int i, char j): a(i), b(j) {
cout << "全特化" << endl;
}
private:
int a;
char b;
};

template<typename T2>
class Test<char, T2> {
public:
Test(char i, T2 j): a(i), b(j) {
cout << "偏特化" << endl;
}
private:
char a;
T2 b;
};

int main() {
Test<int, char> t1(10, 'a'); // 输出:全特化
Test<char, double> t2('b', 3.14); // 输出:偏特化
Test<int, double> t3(10, 3.14); // 输出:模板类
return 0;
}

在这段代码中:

  • Test<int, char> t1(10, 'a') 调用了全特化版本,因为模板参数是 intchar
  • Test<char, double> t2('b', 3.14) 调用了偏特化版本,因为模板参数是 chardouble
  • Test<int, double> t3(10, 3.14) 调用了主模板版本,因为模板参数是 intdouble

结论

通过使用模板特化,C++ 程序员可以针对特定类型提供优化的实现,提高代码的效率和灵活性。全特化用于特定类型的组合,偏特化用于部分类型参数的特定化,而主模板则保持泛型。通过正确使用这些特性,可以编写高效且可维护的泛型代码。

评论