一、函数模板
C++提供了函数模板,通过函数模板建⽴⼀个通⽤函数,其函数类型和形参类型不具体制定,⽤⼀个虚拟的类型来代表,这个通⽤函数就成为函数模板。
- 凡是函数体相同、类型不同的函数都可以⽤这个模板代替,不必定义多个函数,只需在模板中定义⼀次即可。
- 在调⽤函数时系统会根据实参的类型来取代模板中的虚拟类型,从⽽实现不同函数的功能。
写两个demo展示一下非模板函数与模板函数的区别
int my_min(int a, int b){
return a > b ? a : b;
}
double my_min(double a, double b){
return a > b ? a : b;
}
void test(){
// 1. int 类型
int a = 10;
int b = 20;
int min_int_val = min(a, b);
// 2. double 类型
double a = 6.66;
double b = 8.88;
double min_double_val = min(a, b);
}
缺点一幕了然 参数是固定的,如果想加入别的参数只能进行重载 非常麻烦,复用性几乎为零
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
void test(){
// 1. int 类型
int ia = 10;
int ib = 20;
int min_int_val = my_min(ia, ib);
cout << "min_int_val:" << min_int_val << endl;
// 2. double 类型
double da = 6.66;
double db = 8.88;
double min_double_val = my_min(da, db);
cout << "min_double_val:" << min_double_val << endl;
// 3. char 类型
char a = 'c';
char b = 'd';
char min_char_val = my_min(a, b);
cout << "min_char_val:" << min_char_val << endl;
}
二、模板细节
1、class和typename区别
定义模板参数时,使⽤class 和 typename 等价,但 typename 在模板中也有其他的⽤途。
template<class T>
T min(T a, T b){
return a > b ? a : b;
}
template<typename T>
T min(T a, T b){
return a > b ? a : b;
}
2、模板参数
1. 模板参数可以多个。
2. 模板既可以是类型、也可以是编译期常量。
3. 模板参数可以有默认值。
template<class T1, class T2>
T1 my_min(T1 a, T2 b){
return a > b ? a : b;
}
template<class T=int>
T my_max(T a, T b){
return a > b ? a + N : b + N;
}
template<class T, int N>
T my_equal(T a, T b){
return (a == b) ? (a + N) : (b + N);
}
void test()
{
int a = 10;
int b = 20;
int ret = my_equal<int, 100>(a, b);
cout << "ret:" << ret << endl;
}
3、模板类型自动推导
函数模板的类型是调⽤时根据参数的类型⾃动推导出来的,类型推导仅限于函数模板,对于类模板是
不能够进⾏⾃动类型推导的。
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
void test()
{
// 1. int 类型
int ia = 10;
int ib = 20;
int min_int_val = my_min(ia, ib);
cout << "min_int_val:" << min_int_val << endl;
// 2. double 和 int 类型
double da = 30;
// my_min(ia, da);
}
4、函数模板重载
函数模板也和普通函数⼀样⽀持重载,重载的条件和普通函数⼀样:
1. 同⼀个作⽤域
2. 参数个数不同
3. 参数类型不同
4. 参数顺序不同
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
template<class T>
T my_min(T a, T b, T c){
return a > b ? a : b;
}
5.函数模板和普通函数调⽤规则
1. C++编译器优先匹配普通函数。
2. 通过模板空实参数指定编译器只能匹配函数模板。
3. 如果函数模板可以产⽣⼀个更好的匹配,那么选择模板。
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
int my_min(int a, int b){
return a > b ? a : b;
}
void test(){
// 1. 匹配普通函数
int ia = 10;
int ib = 20;
int min_int_val = my_min(ia, ib);
// 2. 匹配函数模板
int ret = my_min<>(ia, ib);
// 3. 匹配函数模板
char a = 'c';
char b = 'd';
char min_char_val = my_min(a, b);
}
6、函数模板和普通函数区别
1. 普通函数⽀持隐式类型转换。
2. ⾃动类型推导,不⽀持隐式类型转换。
3. 显式指定模板参数类型,⽀持隐式类型转换。
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
int my_min(int a, char b){
return a > b ? a : b;
}
void test()
{
int a = 10;
char b = 'c';
// 1. 匹配普通函数
my_min(a, b);
// 2. 匹配函数模板
my_min<int>(a, b);
}
模板的编译机制
C++并不是把函数模板当成能够处理任何类型的函数,函数模板根据具体的调⽤情况,产⽣出对应的具体的函数版本,供程序调⽤。
函数模板编译机制:
我们将第⼀个例⼦中的程序进⾏编译,编译后根据实际调⽤情况⽣成以下三个函数,分别对应
int 、 double 、 char 类型的版本
_Z6my_minIiET_S0_S0_
_Z6my_minIdET_S0_S0_
_Z6my_minIcET_S0_S0_
函数模板和普通函数共存:
1. 如果普通函数能够匹配调⽤,编译器不会再⽣成模板函数。
2. 当调⽤函数时通过空参数列表强制匹配函数模板,此时编译器会再根据函数模板产⽣⼀个模板函
数。
template<class T>
T my_min(T a, T b){
return a > b ? a : b;
}
int my_min(int a, int b){
return a > b ? a : b;
}
void test()
{
// 1. int 类型
int ia = 10;
int ib = 20;
int min_int_val = my_min(ia, ib);
cout << "min_int_val:" << min_int_val << endl;
int ret = my_min<>(ia, ib);
// 2. double 类型
double da = 6.66;
double db = 8.88;
double min_double_val = my_min(da, db);
cout << "min_double_val:" << min_double_val << endl;
// 3. char 类型
char a = 'c';
char b = 'd';
char min_char_val = my_min(a, b);
cout << "min_char_val:" << min_char_val << endl;
}
代码编译过后会生成4个函数
_Z6my_minii
_Z6my_minIiET_S0_S0_
_Z6my_minIdET_S0_S0_
_Z6my_minIcET_S0_S0_
模板的独立编辑问题
1.独⽴编译即 C++编译器编译 a.cpp时,并不知道 b.cpp中定义了哪些符号,当链接时,如果发现
其他 cpp中没有定义则会报错: ⽆法链接的外部符号 。
2. 编译器会对函数模板进⾏两次编译,在声明的地⽅对模板代码本身进⾏编译,在调⽤的地⽅对参
数替换后的代码进⾏编译。
这会导致模板分⽂件编写时的⼀些问题:
具体demo不再书写,只要记住,大部分情况下模板声明和实现在一个文件内即可。后缀为**.hpp**