在C++编程的世界里,为了提高代码的复用率,提高程序员的开发效率,C++引入了函数模板与类模板这种泛型编程;即在定义函数和类的时候,用一个标志来表示参数的类型,不是实际的类型。这种方法是一种能使代码不再局限于任何特定类型的编程。本文章详细描述如何使用函数模板与类模板。 注:笔者入行时间不太长,以下内容大多为学习过程中的心得体会与总结分享给大家,本此也是第一次发文章,如有错误欢迎大家批评指正!感谢!
目录
一. 函数模板
1.1 定义一个函数模板
定义格式:template <class T1,typename T2>
函数返回值类型 函数名 (参数) { }
template <class T1, typename T2>
int fun(T1 a, T2 b)
{
}
上述代码解释:
template:关键字,声明创建模板。
typename/class:二者作用相同,表明后面修饰的类型是一个通用类型,或叫做虚拟类型。
T1/T2:通用类型或者虚拟类型名,可以在函数中直接当做普通类型使用,用于修饰变量。
1.2 函数模板的调用方法
函数模板有如下两种调用方法:
第一种:函数名 <实际类型> (参数);
显式类型推导 fun <int> (1, 2);
第二种:函数名(参数);
隐形类型推导 fun (2.2, 1.5);
#include <iostream>
using namespace std;
template <typename T>
void add_fun(T a, T b) //函数模板
{
cout << "a + b = " << a + b << endl;
}
int main(int argc, char const *argv[])
{
add_fun<int>(10, 2); //显示类型推导
add_fun(12.1, 9.8); //隐式类型推导
return 0;
}
注意:显示类型推导参数和推导的类型必须一致。
1.3 普通函数和函数模板的比较
1.普通函数只可以有一种数据类型相匹配。而模板函数可以有多种类型。
2.隐式推导优先使用普通函数,只有当普通函数不匹配的时候才使用函数模板。
3.函数模板只有在调用时,才会构建函数,而普通函数在编译时构建函数。
4.普通函数调用时候可以发生自动类型转换,而函数模板不行。
1.4 函数模板重载
函数模板的重载和普通函数的重载相似。也是同一个作用域类函数名相同参数列表不同,主要为两种类型的重载
1,参数顺序不同的重载:
template <class T1,class T2> void fun(T2 a,T1 b)
template <class T1,class T2> void fun(T1 a,T2 b)
注意:在函数参数顺序不同的重载中,模板实例化的时候不可以是相同类型。 例如:fun<int , int > (1,2),这中情况下编译器无法识别应该匹配哪个重载版本的函数,造成冲突。
2,参数个数不同的重载
template <class T1,class T2> void fun(T1 a,T2 b)
template <class T1> void fun(T1 a)
这种情况下的重载,给函数传入不同个数的参数,则调用不同版本的重载。
1.5 函数模板特化
作用:为了解决函数模板的部分局限性,在定义函数模板时候,直接确定好T的类型。也就是特定的类型模板。
格式: template<class T> 返回值 函数名(参数类型 参数) { }
#include <iostream>
using namespace std;
template <typename T>
void add_fun(T a, T b) //函数模板
{
cout << "a + b = " << a + b << endl;
cout << "我是函数模板" << endl;
}
template <typename T>
void add_fun(double a, double b) //函数模板
{
cout << "a + b = " << a + b << endl;
cout << "我是特化函数模板" << endl;
}
int main(int argc, char const *argv[])
{
add_fun<int>(12, 9);
add_fun<double>(12.1, 9.8);
return 0;
}
注意:如果特化后,类型确定才可以使用自定义类型中的成员变量和方法。
1.6 多文件函数模板
通常情况下在定义函数或者类的时候,会用.h和cpp文件将定义和申明分开写。而用多文件实现函数模板的情况下则会出现错误。
出现连接错误的原因:函数模板是在调用时构建的,而调用时.h文件中没有函数实现,出现连接错误,找不到函数体。
第一种解决办法:在调用的文件中同时添加 #include <xxx.h> 和 #include <xx.cpp>
第二种解决办法:模板的定义和声明都放在.h头文件中。
1.7 函数模板的嵌套
通常情况下,我们可以在函数模板中还可以去调用另外一个函数模板。在下段代码中,调用add_fun三个参数的重载版本里,先调用add_fun两个参数重载版本的函数模板计算a,b两个值之和赋值给temp,通过temp再与c的值相加计算三个值的和。
#include <iostream>
using namespace std;
template <typename T>
T add_fun(T a, T b)
{
return a + b;
}
template <typename T>
T add_fun(T a, T b, T c)
{
T temp = add_fun<T>(a, b); //在函数模板中调用另一个重载版本的函数模板
cout << "a + b + c = " << temp + c << endl;
return temp + c;
}
int main(int argc, char const *argv[])
{
add_fun<int>(12, 9, 6);
return 0;
}
1.8 函数模板的非类型参数
在某种特定的情况下,我们希望给函数传入一个特定的类型确定参数,但不希望通过直接传参的方式。
这种情况下我们可以通过非类型参数实现。在模板<>中加入一个参数,但这个参数不是通用类型参数,是具体的某一个值。
格式:template <class T , 基本数据类型 变量名>
返回值 函数名(T& 变量名)
例如:template <class T,int size>
void showArr(T* arr);
#include <iostream>
using namespace std;
template <typename T, int size>
void show_arr(T *a)
{
for (int i = 0; i < size; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
int main(int argc, char const *argv[])
{
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
const int arr_size = sizeof(arr) / sizeof(arr[0]);
show_arr<int, arr_size>(arr);
return 0;
}
注意:上例子中,arr_size是通过模板传入到函数中,可以当普通变量使用,非类型参数都是常量,在函数中不允许修改,只可以使用,所以定义非类型参数变量时候,必需要加const进行修饰。
PS:为了简化文章的可读性,所以缩短篇幅内容,类模板的内容我将在下一篇文章中更新。