7.1泛型编程——函数模板

本文深入探讨了C++中的函数模板,包括其作用、语法和使用方式。通过实例解析了如何创建和调用函数模板,以及如何处理类型推导和显示指定类型。同时,文章指出了在使用函数模板时可能出现的错误,如类型不一致导致的二义性问题,并给出了相应的解决策略。此外,还对比了函数模板与普通函数在调用规则上的区别。最后,提到了模板的局限性和解决方法,如重载和具体化。
摘要由CSDN通过智能技术生成

函数模板

作用:
建立一个通用函数,其函数返回值和形参类型可以不具体定制,用一个虚拟的类型来代表

语法:
1.template
2.函数声明或定义

解释:
template:声明创建模板
typename:表明其后面的符号是一种数据类型,可以用class代替
T:通用数据类型,名字可以替换,通常为大写字母

还是不太清楚我们就直接上案例:

写一个交换两个数的函数:

1.交换两个整形:

void swap_int(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
 }

2.交换两个浮点型:

void swap_double(double &a,double &b)
{
	double temp=a;
	a=b;
	b=temp;
 } 

很明显重复了吧,而且还有很多类型:字符型,string,自定义类型…

这个时候我们的函数模板就是可以用了:

既然只是数据类型不同那么我们就用一个T来代表通用:

可以理解为将我们的类型参数化!

//申明一个模板,告诉编译器 ,后面代码中的T不要报错,是一个通用类型 
template <typename T> 
void swap(T &a,T &b)
{
	T temp=a;
	a=b;
	b=temp;
}

这就是函数模板,什么时候使用T呢?使用的时候来指定这个T的具体数据类型。

两种方式使用模板:

1.自动类型推导:
swap1(a,b);
(有想法的朋友已经在想要是我们的a和b类型不一样怎么办,后面我们会在易错的部分讲到)

2.显示指定类型:
swap1(a,b);
编译器就把这个T当成int类型,比上面就多了一个尖括号然后中间放入需要指定的类型

#include <iostream>
using namespace std;
template <typename T> 
void swap1(T &a,T &b)
{
	T temp=a;
	a=b;
	b=temp;
}
int main()
{
	int a;
	int b;
	cin>>a>>b; 
	swap1<int>(a,b);
	cout<<"a:"<<a<<"  "<<"b:"<<b;
}

易错部分:

1.函数在隐式类型转化中,我们的函数的参数的类型必须相同才能够使用,否则会产生而二义性
例子:

#include <iostream>
using namespace std;
template <typename T> 
T add(T a,T b)
{
	return a + b;
}
int main()
{
	int a;
	double  b;
	cin>>a>>b; 
	cout << "a+b:" << add(a, b);
}

使用上面这个函数就会报错,因为我们的a和b类型不一样,发生隐式类型转化的时候会产生二义性。

如何解决:

add(a, b),都强制转化成int就可以了

2.函数模板必须指定T的数据类型才能使用

什么意思呢?
我们可以举一个例子:

#include <iostream>
using namespace std;
template <typename T>
void fun()
{
	cout<<"fun函数的调用"<<endl; 
}
int main ()
{
	fun();
}

程序会报错,为什么呢?
因为我们编译器默认将我们的函数fun()当作是一个函数模板,因为在这个地方没有隐式转化的方法(没有参数),所以我们如果直接只用fun()的话会被认为是调用函数模板。

如何解决呢?
fun();我们只需要显示指定类型,就可以了,虽然这个int没什么用。打个比喻,你抄袭一个东西你不能完全照搬啊,你至少要把名字改了吧。

普通函数和函数模板的区别:

1.普通函数调用时可以发生自动类型转(隐式类型转换)
我们使用普通函数的时候,普通函数已经帮我们指定了形参的类型,所以会自动帮我们强制转化,这个不举例,太简单。

2.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
这个就是易错点1的内容

3.如果利用显示指定类型的方式,可以发生隐式类型转换
易错点1的解决办法

其实上面的区别基本都是围绕着隐式类型转化来讨论的。

是不是感觉很简单,那么我再举一个陷阱例子:

#include <iostream>
using namespace std;
template <typename T> 
void swap1(T &a,T &b)
{
	T temp=a;
	a=b;
	b=temp;
}
int main()
{
	int a;
	double  b;
	cin>>a>>b; 
	swap1<int >(a,b);
	cout<<"a:"<<a<<"  "<<"b:"<<b;
}

可以放在编译器中跑一下,为什么这里我们显示指定了类型是int为什么还是会报错?

普通函数和函数模板的调用规则(函数名都一样)

先贴上代码:

#include <iostream>
using namespace std;
void myprint(int a, int b)
{
	cout << "普通函数的调用" << endl;
}
template <typename T> 
void myprint(T a, T b)
{
	cout << "函数模板的调用" << endl;
}

1.如果函数模板和普通函数都可以实现,优先调用普通函数。(通用的和专属的选择专属的)

在这里插入图片描述
没错吧,首先调用的是普通函数。

那么来思考这个情况。

#include <iostream>
using namespace std;
void myprint(int a, int b);
template <typename T> 
void myprint(T a, T b)
{
	cout << "函数模板的调用" << endl;
}
int main()
{
	int a = 10;
	int b = 10;
	myprint(a, b);
}

大部分一样,只是普通函数的实现删掉了,并且改成了一个函数的声明。

那么这种情况会因为没有函数的实现体而去调用函数模板吗?

答案:不会,还是会调用普通函数,并且会出现报错:在这里插入图片描述
一个无法解析的外部命令,这个报错是出现在链接阶段,意思就是我要调用你,但是我却找不到你的实现体。所以就算只是一个声明,没有实现体当满足调用普通函数的时候还是会调用普通函数。

2.可以通过空模板参数列表来强制调用函数模板

什么意思呢?
在这里插入图片描述
加个尖括号就ok,空模板就是尖括号里面什么都没有

3.函数模板可以发生重载

在这里插入图片描述
注意哦这里重载的时候还是要写一次:
template

4.如果函数模板可以产生更好的匹配,优先调用函数模板

什么叫做更好的匹配?
在这里插入图片描述
会调用函数模板。为什么?

如果编译器由高字节的int到低字节的char进行隐式转换,精度会丢失,某些编译器会报错,但编译还是能通过。编译器会尽量避免这种情况的产生。

函数模板的局限

1.如果传入的是一个数组就无法实现

2.如果传入的是自定义数据类型也无法正常运行

c++为了解决这些问题,提供了模板的重载,可以为这些特定的类型提供具体化的模板

解决方法一:
重载运算符
解决方法二:
利用具体化实现代码,具体化会优先调用。

具体化语法:template<> 返回值类型 函数名字(形参具体类型 &a, 形参具体类型 &b);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值