模板(Template)概述

模板是 C++ 中的一种泛型编程机制,允许编写通用的代码,可以在不指定具体数据类型的情况下定义函数、类或结构体。模板的主要目的是提供代码的复用性和灵活性,从而支持处理多种数据类型的能力。

C++ 中的模板主要分为两种:

  1. 函数模板(Function Template)
  2. 类模板(Class Template)

1. 函数模板

函数模板是定义一个可以操作多种数据类型的函数。通过模板,函数可以在调用时根据传递的参数类型自动生成具体的函数。

1.1 函数模板的基本语法

1
2
3
4
5
6
7
8
9
10
template <typename T>
T add(T a, T b) {
return a + b;
}

int main() {
cout << add<int>(3, 4) << endl; // 显式指定类型为 int
cout << add(3.5, 4.2) << endl; // 隐式推导类型为 double
return 0;
}

输出:

1
2
7
7.7

1.2 关键点

  • template <typename T>:定义模板,其中 T 是占位符,表示数据类型。
    • 也可以使用 class 代替 typename(两者在模板声明中等价)。
  • 模板函数的具体类型可以显式指定(如 add<int>(3, 4)),也可以通过参数隐式推导(如 add(3.5, 4.2))。

1.3 函数模板的特例化

有时我们希望对某些特定类型提供专门的实现,可以通过模板特例化实现。

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 T>
T max(T a, T b) {
return (a > b) ? a : b;
}

// 特例化版本(针对 const char* 类型)
template <>
const char* max<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b; // 字符串比较
}

int main() {
cout << max(3, 7) << endl; // 输出 7
cout << max("apple", "banana") << endl; // 输出 banana
return 0;
}

说明:

  • 特例化版本定义了针对 const char* 类型的专门实现。
  • 当模板匹配到 const char* 类型时,会优先使用特例化版本。

2. 类模板

类模板是为实现通用的类而设计的,允许类操作不同类型的数据而不需要重复编写代码。

2.1 类模板的基本语法

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

template <typename T>
class MyClass {
private:
T data;
public:
MyClass(T value) : data(value) {}

void display() {
cout << "Value: " << data << endl;
}
};

int main() {
MyClass<int> obj1(10); // int 类型
MyClass<double> obj2(3.14); // double 类型

obj1.display(); // 输出 Value: 10
obj2.display(); // 输出 Value: 3.14

return 0;
}

说明:

  • template <typename T> 定义了一个类模板。
  • 模板类实例化时,需要为模板参数 T 提供具体类型,如 MyClass<int>

2.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
#include <iostream>
using namespace std;

// 通用类模板
template <typename T>
class MyClass {
public:
void display() {
cout << "Generic template" << endl;
}
};

// 特例化版本(针对 int 类型)
template <>
class MyClass<int> {
public:
void display() {
cout << "Specialized template for int" << endl;
}
};

int main() {
MyClass<double> obj1; // 使用通用模板
MyClass<int> obj2; // 使用特例化模板

obj1.display(); // 输出 Generic template
obj2.display(); // 输出 Specialized template for int

return 0;
}

说明:

  • 通用模板适用于所有类型,但当实例化类型为 int 时,会使用特例化版本。

2.3 类模板的部分特例化

类模板还支持部分特例化,即只对部分模板参数进行特例化。

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
#include <iostream>
using namespace std;

// 通用类模板
template <typename T, typename U>
class MyClass {
public:
void display() {
cout << "Generic template" << endl;
}
};

// 部分特例化(当第二个类型参数是 int 时)
template <typename T>
class MyClass<T, int> {
public:
void display() {
cout << "Partial specialization for second type int" << endl;
}
};

int main() {
MyClass<double, double> obj1; // 通用模板
MyClass<double, int> obj2; // 部分特例化模板

obj1.display(); // 输出 Generic template
obj2.display(); // 输出 Partial specialization for second type int

return 0;
}

说明:

  • 部分特例化可以只针对某些类型参数组合,提供更灵活的设计。

3. 模板的其他用法

3.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 T = int>
class MyClass {
public:
void display() {
cout << "Type: " << typeid(T).name() << endl;
}
};

int main() {
MyClass<> obj; // 使用默认类型 int
MyClass<double> obj2; // 显式指定类型为 double

obj.display(); // 输出 Type: int
obj2.display(); // 输出 Type: double

return 0;
}

3.2 模板模板参数

模板参数本身可以是模板。

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

template <template <typename, typename> class Container>
class MyClass {
public:
void display() {
Container<int, allocator<int>> c = {1, 2, 3};
for (int x : c) {
cout << x << " ";
}
cout << endl;
}
};

int main() {
MyClass<vector> obj; // 使用 vector 作为模板模板参数
obj.display(); // 输出 1 2 3
return 0;
}

说明:

  • template <template <typename, typename> class Container> 定义了模板模板参数。
  • 实例化时,vector 作为模板模板参数被传递。

4. 编译期计算与模板

模板可以用于编译期计算,例如实现递归的斐波那契数列。

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

template <int N>
struct Fibonacci {
static const int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};

template <>
struct Fibonacci<0> {
static const int value = 0;
};

template <>
struct Fibonacci<1> {
static const int value = 1;
};

int main() {
cout << Fibonacci<10>::value << endl; // 输出 55
return 0;
}

说明:

  • 模板通过递归定义实现了编译期的斐波那契数列计算。
  • Fibonacci<10> 会在编译时计算出结果。

总结

模板是 C++ 强大的泛型编程工具,其主要特点包括:

  1. 函数模板类模板
  2. 支持特例化(全特例化和部分特例化)。
  3. 提供了编译期计算能力。
  4. 与 STL 紧密结合,广泛用于算法和容器中。

模板大大提高了代码的复用性和通用性,但模板的编译错误通常比较复杂,因此需要小心使用。