C++函数模板
在我们对不同数据类型的程序逻辑和操作相同时,使用函数模板会非常的方便,只需要定义单个函数模板。函数模板实际上就是对数据类型进行了参数化。 模板的定义都以template开头,后面跟着尖括号括起来的类型参数,类型参数的参数名可以是typename、也可以是class,他们作用是一致的。
#include <iostream>
using namespace std;
template<typename T>
bool compare(T a, T b)
{
return a > b;
}
int main()
{
compare<int>(10, 20);
compare(10.0, 10.2);
compare("aaa", "bbb");
return 0;
}
模板函数调用及实参推演
函数模板调用在函数名后面跟上尖括号,尖括号里传入指定的数据类型,和函数实参,如compare<int>(10, 20); 。 但是不传入类型实参也可以实现模板函数的调用,如compare(10.0, 10.2); ,这是因为函数模板可以进行实参推演,可以根据用户传入实参的类型,推导出实参的类型参数的具体类型。 但这样的代码:compare(10, 10.2); 编译是不能通过的。因为函数模板中形参的类型是相同的,但传入的实参类型不同,编译器不知道要用哪个类型来实例化,我们可以通过指定具体类型的模板函数来解决这个问题,如:compare<int>(10, 10.2); 或compare<double>(10, 10.2);
未指定模板类型时,编译器会优先匹配普通函数,没有匹配的普通函数再去类型推导实例化模板函数。
函数模板实例化
函数模板的实例化是在编译阶段完成的,在函数调用点,编译器会从用户指定的类型,从函数模板实例化一份模板函数代码出来,相同的类型参数只会实例化出一份代码,否则会出现函数重定义。 如上面的代码中,compare<int>(10, 20); 实例化的模板函数代码如下:
bool compare<int>(int a, int b)
{
return a > b;
}
函数模板中花括号中的代码,是不参与编译的。因为传入的类型参数可能是类,也可能是普通的数据类型,比如在 编译a > b; 时,因为a和b的类型不确定,编译器不知道要进行的是普通的比较,还是执行对象的operator>,所有函数模板的函数体是不参与编译的。
函数模板特例化
在对compare("aaa", "bbb"); 实例化模板函数时,实例化出这样的代码:
bool compare<const char*>(const char* a, const char* b)
{
return a > b;
}
在对const char* 进行 > 比较是没有太大意义的,我们实际想要的是 strcmp(a, b) > 0; ,这时模板函数的逻辑和操作已经不能满足我们所有的需求了。 对于某些特定类型来说,以来函数模板默认实例化的模板函数代码,不能满足它的处理逻辑时,我们就需要定义模板的特例化 例如对上面compare函数定义的const char*类型的模板特例化:
template<>
bool compare(const char* a, const char* b)
{
return strcmp(a, b) > 0;
}
在模板特例化时,应定义为template<> ,而不是template<typename T> ,否则在模板类型推导时,就会实例化出默认的模板函数。函数模板推导是根据形参列表来推导的,所以如果模板特例化时定义为template<typename T> ,而形参列表中没有类型T,就不会进行类型推导了。
跨文件使用模板的坑
在头文件声明模板,在另一个文件定义,再在另一个文件包含头文件使用时,会发生链接错误。 因为函数模板实例化是在编译阶段完成的,而.cpp文件是单独编译,在定义模板的文件中并没有发生函数调用,也就不会实例化出模板函数,链接时就会发生错误。 所以模板的声明和定义要都包含在头文件中。 预编译时就会把模板包含到文件中。 compare.h
template<typename T>
bool compare(T a, T b);
compare.cpp
#include "compare.h"
template<typename T>
bool compare(T a, T b)
{
return a > b;
}
main.cpp
#include "compare.h"
int main()
{
compare(10, 20);
}
函数模板的非类型参数
函数的非类型参数都是常量,只能使用,不能修改。如:
#include <iostream>
using namespace std;
template<typename T, int SIZE>
void sort(T* arr)
{
for (int i = 0; i < SIZE - 1; i++)
{
for (int j = 0; j < SIZE - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
T tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 4,5,1,3,6,7,2,8,9 };
const int size = sizeof(arr) / sizeof(arr[0]);
sort<int, size>(arr);
for (int i = 0; i < size ; i++)
{
cout << arr[i] << " ";
}
}
调用模板函数传参时必须要穿常量,否则编译不通过。
|