C++知识点55——函数模板

本文详细介绍了C++中的函数模板,包括其定义、使用方法、实参推断机制、重载原则等内容,并探讨了如何解决模板参数类型不匹配等问题。

一、为啥需要模板

防止相同功能的重复实现

 

二、函数模板

1、模板函数的定义

template <typename T>
const T& Max(const T &a, const T &b)
{
	return a>b?a:b;
}

上述代码就是个最简单的函数模板

template关键字表示模板,<>中的typename用来表示模板参数,模板参数名字是T。函数体中指向operator>,所以T必须支持operator>

在这里typename可以用class来替换,但是不能用struct来替换

 

2、模板函数的使用

int main(int argc, char const *argv[])
{
	int a=Max(10, 20);
	double b=Max(3.4, 5.6);
	string s=Max("1234", "5678");
	cout<<a<<","<<b<<","<<s<<endl;
	return 0;
}

上述代码中,Max被调用了三次,每次分别用int,double、string作为具体类型,来代替模板参数。用具体类型替代模板参数的过程叫实例化。每次实例化都会产生不同的模板函数实例,所以针对三种类型的每一种,Max都被编译了一次

如果使用了一个不支持operator>的类作为模板参数,那么编译时会报错

test one, two;
Max(one, two);

test是一个自定义不支持operator>的类。

因此可知,当一个函数模板被编译时,编译过程是这样的:1、查看模板代码本身是否有语法错误。2、实例化时,查看是否所有调用都有效。

上述两点导致函数模板在编译时,需要查看函数模板的定义,因此,一般将函数模板的实现也放在头文件的内部

这一点也和普通的函数不一样,普通函数只需要给出声明即可通过编译,但是函数模板在实例化时需要查看内部的实现是否都有效,要给出模板的定义

因为模板代码编译的时候会根据不同的模板参数实例化出不同的代码,所以,要保证模板参数支持具体的函数调用。当以不同的模板参数实例化函数模板,从而调用不同的函数,便是所谓的编译期多态。

 

3、函数模板的实参推断

当调用Max时,会给Max传参,编译器会根据传入实参的类型对模板参数的类型T进行推断,当传入的时两个int变量,编译器会自动推断模板参数T的类型是int,double和string类型同理。模板的类型参数可以用来指定类型或者返回值的类型,以及在函数体内部进行变量的定义

此外,模板参数不允许进行隐式类型转换,当进行如下调用时,不允许将double转为int,因此编译器提示出现匹配错误

Max(10, 20.2);

解决该错误的方式有三种:

tip1:使用static_cast将int转double或者double转int

Max(static_cast<double>(10), 20.2);

tip2:显示指定T的类型为double或者int

Max<double>(10, 20.2);
Max<int>(10, 20.2);

tip3:添加一个新的模板参数

template <typename T1, typename T2>
const T2& Max(const T1 &a, const T2 &b)
{
	return a>b?a:b;
}
Max(10.2, 20);

在编译tip3中的代码时,会出现警告

之所以出现问题是因为:实例化Max时,函数的返回值类型有可能和T2不同,上述代码中,返回值类型是const int&,但是返回的有可能是a,也有可能是b,如果返回的是a且a的绑定变量的类型是double,那么当函数返回时,就会出现如下类型转换

const T1 tmp=T2; 
const T2 &res=tmp;

上述转换在一般情况下没问题,但是因为tmp在函数内部,是个局部临时变量,函数作用域结束后就无效了,所以不能当做返回值让const T2的引用绑定,所以就会出现上面的警告,关于const与引用见博客https://blog.youkuaiyun.com/Master_Cui/article/details/106353580

解决办法就是将返回值的类型变成const T1或者const T2

template <typename T1, typename T2>
const T2 Max(const T1 &a, const T2 &b)
{
	return a>b?a:b;
}

上述代码的问题就在于当实例化一个Max函数时,返回值有可能被隐式转化

 

或者直接加个返回值模板参数用来指定返回值

template <typename T1, typename T2, typename RT>
const RT Max(const T1 &a, const T2 &b)
{
	return a>b?a:b;
}

这种办法带来的问题就是需要显示指定模板参数RT,因为不能对函数模板的返回值进行模板实参推断,所以调用Max时很麻烦

Max<int, double, int>(10, 20.2);

必须得按顺序显示指定模板参数的类型

此时如果还调用Max(10, 20.2);编译就无法通过,因为RT的类型没有指定

为了简单一些,可以将RT放在最前面

template < typename RT,typename T1, typename T2>
const RT Max(const T1 &a, const T2 &b)
{
	return a>b?a:b;
}
Max<int>(10, 20.2);

这样在实例化函数时,只需要指定返回值RT的类型即可,而不需要指定所有的模板参数

 

4、函数模板的重载

函数模板也可以重载。当出现重载时,调用原则如下:当一个非模板函数和一个模板函数重名且该模板函数可以实例化为该非模板函数,那么在调用时,会调用非模板函数,而不会从模板函数中产生一个实例

int mymax(const int& a, const int &b)
{
	cout<<__func__<<"in func"<<endl;
	return a>b?a:b;
}

template <typename T>
const T mymax(const T& a, const T &b)
{
	cout<<__func__<<"in template"<<endl;
	return a>b?a:b;
}

int main(int argc, char const *argv[])
{
	mymax(10, 20);
	mymax<>(10.1, 20.2);
	return 0;
}

第17行中,尖括号中没有指定任何类型,意味着编译器需要调用模板版本的mymax并进行实参推断

 

参考

《C++ Template》

《C++ Primer》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值