C++ 模板是泛型编程的核心工具,支持编写与类型无关的代码以提升复用性。函数模板与类模板的定义语法、实例化机制及参数匹配原则。通过对比传统重载方案,阐述隐式与显式实例化的区别,解析编译器类型推导过程。重点说明类模板需显式绑定类型参数,以及非模板函数优先匹配规则,帮助开发者掌握模板在实际工程中的应用技巧。
lzdxwyh1 浏览
前言
C++ 模板是泛型编程的核心工具,允许编写与类型无关的代码,从而提升复用性和灵活性。模板在编译时实例化,根据实际使用的类型生成具体代码,因此不会带来运行时开销。
#include<iostream>usingnamespace std;
template<class T>
voidSwap(T& a, T& b){
T temp = a;
a = b;
b = temp;
}
intmain(){
int m = 10, n = 20;
double a = 1.2, b = 2.5;
Swap(m, n);
Swap(a, b);
return0;
}
示例 2:求最大值
#include<iostream>usingnamespace std;
template <typename T>
T max(T a, T b){
return a > b ? a : b;
}
intmain(){
int i = max(3, 5); // T 被推导为 intdouble d = max(3.14, 2.7); // T 被推导为 doublereturn0;
}
示例 3:多模板参数
#include<iostream>usingnamespace std;
template<class T1, class T2>
voidfunc2(const T1& x, const T2& y){
cout << x << " " << y << endl;
}
intmain(){
int a = 10;
double b = 3.14;
func2(a, b); // T1 被推导为 int 类型,T2 被推导为 double 类型return0;
}
#include<iostream>usingnamespace std;
template<class T>
T Add(const T& left, const T& right){
return left + right;
}
intmain(){
int a1 = 10;
double d1 = 3.14;
Add(a1, d1); // 编译报错return0;
}
报错原因: 在编译期间,当编译器看到该实例化时,需要推演其实参类型。通过实参 a1 将 T 推演为 int,通过实参 d1 将 T 推演为 double 类型,但模板参数列表中只有一个 T,编译器无法确定此处到底该将 T 确定为 int 或者 double 类型而报错。在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅。
解决办法:
用户自己来强制转化:Add(a1, (int)d1)。
显式实例化:Add<int>(a1, d1)。
2. 显式实例化
在函数名后的 <> 中指定模板参数的实际类型。
#include<iostream>usingnamespace std;
template<class T>
T Add(const T& left, const T& right){
return left + right;
}
intmain(){
int a = 10;
double b = 20.0;
// 显式实例化Add<int>(a, b);
return0;
}
这意味着你在调用时明确告诉编译器:'模板参数 T 就是 int(或 double),不要再推导了',此时编译器会跳过类型推导过程,直接使用你指定的类型来实例化函数模板。编译器会检查这个调用是否合法:对于 Add<int>(a1, d1),生成的函数签名是 int Add(const int&, const int&)。实参 a1 是 int,可以直接绑定到 const int&。实参 d1 是 double,但 double 可以隐式转换为 int,因此编译器会生成一个临时 int 对象传递给函数。同理,Add<double>(a1, d1) 会将 a1 从 int 隐式转换为 double,也可以正常调用。