目录
一、函数模板(泛型编程) 概念:
用一个标志表示类型,此类型并非实际类型。可以独立于任何特定类型的编程,是c++比较重要的一部分。(如:ppt模板 ——是一种框架,需要自行填写内容,可以方便使用,不用重复定义,提高复用度)
二、格式:
template<class T> 或者 template<typename T>
返回值 函数名(参数) {函数体}
#include <iostream>
using namespace std;
//写一个函数模板交换两个任意类型变量的值
template< class T1,typename T2>
void fun(T1 &a,T2 &b)
{
T1 temp = a;
a = b;
b = temp;
cout << "我是函数模板" << endl;
}
三、参数:
template :声明创建模板;
typename/class:表明后面的T是一个通用类型,或者称为虚拟类型;
T:通用类型或者虚拟类型,(理解成类型)可以在函数中直接当做普通类型使用。
四、模板的调用方法:
1.显式类型推导:函数名<实际类型1,实际类型2>(参数);
2.隐式类型推导:函数名(参数);
int main(int argc, char *argv[])
{
double a=12.23;
double b=23.12;
cout << "交换前 a=" << a << "\t"<< "b=" << b << endl;
fun(a,b); //隐式推导优先调用普通函数
fun<double,double>(a,b); //显式推导优先调用函数模板
cout << "交换后 a=" << a << "\t"<< "b=" << b << endl;
return 0;
}
五、注意事项:
1.显式类型推导的参数和推导的类型必须一致;
2.如果有普通函数和模板函数构成重载,隐式类型推导优先调用普通函数,想优先调用模板函数,可以用显式类型推导省略实际类型的方式: 函数名<>(参数1,参数2);(如下图)
#include <iostream>
using namespace std;
//写一个函数模板交换两个任意类型变量的值
template< class T1,typename T2>
void fun(T1 &a,T2 &b)//函数模板重载1
{
T1 temp = a;
a = b;
b = temp;
cout << "我是函数模板1" << endl;
}
template <class T1,class T2>
void fun(T2 &a,T1 &b)//函数模板重载2
{
T2 temp = a;
a = b;
b = temp;
cout << "我是函数模板2" << endl;
}
void fun(double &a,double &b)//普通函数
{
double temp = a;
a = b;
b = temp;
cout << " 我是普通函数" << endl;
}
int main(int argc, char *argv[])
{
double a=12.23;
double b=23.12;
cout << "交换前 a=" << a << "\t"<< "b=" << b << endl;
fun(a,b); //隐式推导优先调用普通函数
fun<>(a,b); //显式推导优先调用函数模板
cout << "交换后 a=" << a << "\t"<< "b=" << b << endl;
return 0;
}
六、普通函数与函数模板的区别:
1.普通函数只能与一种数据类型相匹配 而函数模板可以多种类型
2.普通函数在编译时就会构建函数 而函数模板只有在调用时才会构建函数
3.普通函数调用时可以自动类型转换 而函数模板调用时会指定类型
4.普通函数在函数模板是隐式推导时优先级高于函数模板。
七、函数模板的重载:
1.概念:和普通函数的重载相似,也是同一个作用域内函数名相同参数列表不同。
2.定义:template<class T1,class T2> void temp(T1 a,T2 b);
template<class T1,class T2> void temp(T2 a,T1 b);
template<class T1> void temp(T1 a);
3.注意:在函数参数顺序不同的重载中,实例化(即调用函数的时)时不可以是相同类型(相同类型则无法判断调用哪一个);
八、函数模板的特化:
1.概念:为了解决函数模板的局限性(参数类型不是基本数据类型时),在定义函数模板的时候,直接确定T的类型,也就是特定的函数模板;
2.格式:template <class T> 返回值 函数名(类名& 对象){ 函数体 }
3.优先级:当传入的自定义类型与特化版本匹配,会优先使用特化版本;
4.特化后 :只有当类型确定才可以使用自定义类型中的成员变量及成员方法等;
5.应用场景:当需要对两个对象进行比较时就需要传入特化模板才可进行比较。
#include <iostream>
#include <string>
using namespace std;
class person//定义一个类
{
public:
int age = 0;
string name;
person(int age,string name):age(age),name(name) {
}
};
/***********定义一个比较的函数模板********/
template <class T>
bool cmp(T a,T b)
{
if(a == b)
{
return true;
}
return false;
}
/*************定义一个特化函数模板**************/
template<class T>
bool cmp(person& a,person& b)
{
if(cmp<int>(a.age,b.age) && cmp<string>(a.name,b.name))//调用定义的比较函数模板
{
return true;
}
return false;
}
int main(int argc, char *argv[])
{
person a(10,"temp");
person b(10,"temp");
//打印比较的结果:结果为1则两个对象的age和name相等,0则表示不相等
cout << cmp<person>(a,b) << endl;
return 0;
}
九、多文件实现函数模板:
1.定义:在函数模板中定义函数或者类的时候,不能把.h和cpp文件定义和申明分开。
2.后果:编译时会出现连接错误(错误原因是:函数模板是在调用时确定的版本,而调用时.h中没有函数实现,出现连接错误,找不到函数体);
3.解决方法:
1.把声明和定义都写在xx.h头文件中;
2.把xx.h和xx.cpp文件删除一个,其中一个改为xx.hpp文件即可;
3.在main中#include <xxx.h> 和 #include <xx.cpp>。
十、函数模板的嵌套:
定义:直接在函数模板中调用另一个模板即可(如下)
注意:函数模板嵌套使用时两个函数模板的名字不能一样,不然无法识别。
#include <iostream>
using namespace std;
template <class T>
void fun(T &a)
{
cout << "我是函数模板1" << endl;
}
template <class T>
void show(T &a)
{
fun(a);
cout << "我是函数模板2" << endl;
}
int main(int argc, char *argv[])
{
int a=12;
show(a);//调用show函数模板时在函数内部会调用fun函数模板
return 0;
}
十一、函数模板的非类型参数:
1.定义:模板参数不仅可以使类型,也可以是值。可以用特定的类型名(比如int)而非typename或者class表示非类型模板参数。当模板被实例化时,非类型模板参数会被一个值而不是类型替代。非类型模板参数
2.格式:template <class T,基本数据类型 变量名> 返回值 函数名(T& 变量);
3.如:template <class T,int size> 返回值 函数名(T & a){函数体}
4.注意:上列中size是通过模板传入到函数中,可以当做普通变量使用;
非类型参数都是常量,在函数中不允许修改,只可以使用,定义非类型参数变量时候,需要加const。
5.代码部分:
#include <iostream>
#define len(x) sizeof(x)/sizeof(x[0])
using namespace std;
//如下:利用非类型参数把数组的size大小传入到函数模板中,就可对其进行排序
template <class T,int len>
void Sort(T *a)
{
for(int i=0;i<len;i++)
{
for(int j=i+1;j<len;j++)
{
if(a[i] < a[j])
{
T temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
for(int i=0;i<len;i++)
{
cout << a[i] <<"\t" ;
}
cout << endl;
}
int main(int argc, char *argv[])
{
int arr[5]={2,45,2,55,5};
const int len1=len(arr);//传入的变量需要用const修饰
Sort<int,len1>(arr);
return 0;
}