普通函数与函数模板的区别
普通函数与函数模板区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
示例:
//普通函数
intmyAdd01(inta, intb)
{
returna+b;
}
//函数模板
template<classT>
TmyAdd02(Ta, Tb)
{
returna+b;
}
//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
voidtest01()
{
inta=10;
intb=20;
charc='c';
cout<<myAdd01(a, c) <<endl; //正确,将char类型的'c'隐式转换为int类型 'c' 对应 ASCII码 99
//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换
myAdd02<int>(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
}
intmain() {
test01();
system("pause");
return0;
}
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
示例:
//普通函数与函数模板调用规则
voidmyPrint(inta, intb)
{
cout<<"调用的普通函数"<<endl;
}
template<typenameT>
voidmyPrint(Ta, Tb)
{
cout<<"调用的模板"<<endl;
}
voidtest01()
{
//1、如果函数模板和普通函数都可以实现,优先调用普通函数
// 注意 如果告诉编译器 普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
inta=10;
intb=20;
myPrint(a, b); //调用普通函数
//2、可以通过空模板参数列表来强制调用函数模板
myPrint<>(a, b); //调用函数模板
}
intmain() {
test01();
system("pause");
return0;
}
//普通函数与函数模板调用规则
voidmyPrint(inta, intb)
{
cout<<"调用的普通函数"<<endl;
}
template<typenameT>
voidmyPrint(Ta, Tb)
{
cout<<"调用的模板"<<endl;
}
template<typenameT>
voidmyPrint(Ta, Tb, Tc)
{
cout<<"调用重载的模板"<<endl;
}
voidtest01()
{
//3、函数模板也可以发生重载
intc=30;
myPrint(a, b, c); //调用重载的函数模板
//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
charc1='a';
charc2='b';
myPrint(c1, c2); //调用函数模板
}
intmain() {
test01();
system("pause");
return0;
}
分析:如果函数模板可以产生更好的匹配,优先调用函数模板
以上面案例为例,编译器默认调用了普通函数,发现还需要把char转成int,又发现如果调用模板则只需确定类型即可,于是调用模板函数,最优选择。
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
模板的局限性
局限性:
- 模板的通用性并不是万能的
例如:
template<classT>
voidf(Ta, Tb)
{
a=b;
}
在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
再例如:
template<classT>
voidf(Ta, Tb)
{
if(a>b) { ... }
}
在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
示例:
#include<iostream>
usingnamespacestd;
#include <string>
classPerson
{
public:
Person(stringname, intage)
{
this->m_Name=name;
this->m_Age=age;
}
stringm_Name;
intm_Age;
};
//普通函数模板
template<classT>
boolmyCompare(T&a, T&b)
{
if (a==b)
{
returntrue;
}
else
{
returnfalse;
}
}
//第一种方法:重载算术运算符==,缺点:太麻烦
//第二种方法:
//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<>boolmyCompare(Person&p1, Person&p2)
{
if ( p1.m_Name ==p2.m_Name&&p1.m_Age==p2.m_Age)
{
returntrue;
}
else
{
returnfalse;
}
}
voidtest01()
{
inta=10;
intb=20;
//内置数据类型可以直接使用通用的函数模板
boolret=myCompare(a, b);
if (ret)
{
cout<<"a == b "<<endl;
}
else
{
cout<<"a != b "<<endl;
}
}
voidtest02()
{
Personp1("Tom", 10);
Personp2("Tom", 10);
//自定义数据类型,不会调用普通的函数模板
//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
boolret=myCompare(p1, p2);
if (ret)
{
cout<<"p1 == p2 "<<endl;
}
else
{
cout<<"p1 != p2 "<<endl;
}
}
intmain() {
test01();
test02();
system("pause");
return0;
}
总结:
- 利用具体化的模板,可以解决自定义类型的通用化
- 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板