在 C++ 中,模板(Template)是一项极其重要的泛型编程技术。它让代码摆脱具体类型的束缚,使程序能够编写“与类型无关”的逻辑,从而提高代码的复用性与灵活性。模板主要分为两类:函数模板(Function Template)与类模板(Class Template)。本文将系统介绍模板的概念、用法、实例化机制以及编译器的类型推导规则。
1. 函数模板(Function Template)
1.1 函数模板的概念
函数模板代表了一组“函数家族”。它不依赖于具体的数据类型,编译器会在使用模板函数时,根据传入的实参类型自动生成对应类型的函数版本——这种由模板自动生成具体函数代码的过程称为模板实例化。
简单来说:
写一次函数模板,可以生成多个不同类型的函数版本。
比如一个 Swap 函数,它可以交换 int、double、char 等任意类型的参数。传统 C 写法需要写多个版本,而模板可以一次解决。
1.2 函数模板的定义格式
template <typename T>
返回值类型 函数名(参数列表)
{
// 函数体
}
关键点说明:
-
typename用来声明模板参数类型,也可以使用class,两者在模板中完全等价。 -
不能用 struct 替代 typename/class。
1.3 示例:Swap 函数模板
template <typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
使用方式:
int a = 10, b = 20;
Swap(a, b); // T 推导为 int
double x = 1.1, y = 2.2;
Swap(x, y); // T 推导为 double
编译器会根据实参类型自动推导 T,然后生成对应版本的函数,这就是 模板的隐式实例化(implicit instantiation)。
2. 模板实例化机制
模板参数的实例化分为两类:
2.1 隐式实例化
即上面示例中那样,编译器根据实参自动推导类型:
Swap(a, b); // 自动推导 T 为 int
无需写类型。
2.2 显式实例化
手动指定模板参数:
Swap<double>(x, y);
适用于推导不明确或需要强制类型的场景。
3. 模板与普通函数的重载关系
C++ 允许函数模板与一个同名的非模板函数同时存在,例如:
void Print(int x)
{
cout << "int 非模板函数: " << x << endl;
}
template<typename T>
void Print(T x)
{
cout << "模板函数: " << x << endl;
}
调用优先级规则:
3.1如果有完全匹配的非模板函数 → 优先调用普通函数
Print(10); // 走普通函数
3.2如果模板可以产生更好的匹配 → 优先使用模板
比如:
template<typename T>
void Func(T x) {} // 模板版本
void Func(double x) {} // 普通版本
Func(1.1f); // float -> 模板更匹配 → 调用模板函数
3.3普通函数允许自动类型转换,模板不允许自动转换
void Show(int x);
template <typename T>
void Show(T x);
Show(3.14); // 调用普通函数(double 转 int)
4. 类模板(Class Template)
4.1 类模板的定义格式
template <typename T>
class ClassName
{
public:
// 成员变量
// 成员函数
};
类模板和函数模板的区别是:
-
类模板不会自动实例化
-
必须手动指定类型才能生成具体类
4.2 类模板示例
template<typename T>
class Box
{
public:
T data;
Box(T d) : data(d) {}
};
4.3 类模板的实例化
Box<int> box1(10); // 实例化为 Box<int>
Box<double> box2(3.14); // 实例化为 Box<double>
注意:
类模板名字本身不是类,只有实例化后的 Box<int> 等才是真正的类类型。
5. 总结
| 模板类型 | 特点 |
|---|---|
| 函数模板 | 自动推导类型,可隐式实例化 |
| 类模板 | 必须显式指定类型 |
| 模板函数 vs 非模板函数 | 完全匹配 → 优先非模板;更优匹配 → 模板 |
| 模板不支持隐式类型转换 | 普通函数可以转换 |
1018

被折叠的 条评论
为什么被折叠?



