C++ 函数模板的重载与实参推断

    结合网上的资料,对函数模板的重载与实参推断做一个总结。

函数模板的重载

  当需要对不同的类型使用同一种算法时,为了避免定义多个功能重复的函数,可以使用模板。然而,并非所有的类型都使用同一种算法,有些特定的类型需要单独处理,为了满足这种需求,C++允许对函数模板进行重载。

  例如,当交换基本类型和交换两个数组时,虽然功能都是交换,但是使用的方法却不相同,交换两个数组唯一的办法就是逐个交换所有的数组元素。

template<class T> void Swap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}
template<typename T> void Swap(T a[], T b[], int len){
    T temp;
    for(int i=0; i<len; i++){
        temp = a[i];
        a[i] = b[i];
        b[i] = temp;
    }
}

函数模板的实参

  在使用类模板创建对象时,需要显式的指明实参,例如:

template<typename T1, typename T2> class Point;
Point<int, int> p1(10, 20);  //在栈上创建对象
Point<char*, char*> *p = new Point<char*, char*>("东京180度", "北纬210度");  //在堆上创建对象

对于函数模板,调用函数时可以不显式的指明实参,编译器会自动推导出T的类型,例如:

//函数声明
template<typename T> void Swap(T &a, T &b);
//函数调用
int n1 = 100, n2 = 200;
Swap(n1, n2);
float f1 = 12.5, f2 = 56.93;
Swap(f1, f2);

模板实参推断过程中的类型转换

普通函数调用时的类型转换

  • 算数转换:例如 int 转换为 float,char 转换为 int,double 转换为 int 等。
  • 派生类向基类的转换:也就是向上转型,
  • const 转换:也即将非 const 类型转换为 const 类型,例如将 char * 转换为 const char *。
  • 数组或函数指针转换:如果函数形参不是引用类型,那么数组名会转换为数组指针,函数名也会转换为函数指针。
  • 用户自定的类型转换。

例如:

void func1(int n, float f);
void func2(int *arr, const char *str);
int nums[5];
char *url = "http://c.biancheng.net";
func1(12.5, 45);
func2(nums, url);

  对于 func1(),12.5 会从double转换为int,45 会从int转换为float;对于 func2(),nums 会从int [5]转换为int *,url 会从char *转换为const char *

函数模板调用时的类型转换

  对于函数模板,类型转换收到了更多的限制,仅能进行[const 转换]和[数组或函数指针转换],其他都不能应用于函数模板。

template<typename T> void func1(T a, T b);
template<typename T> void func2(T *buffer);
template<typename T> void func3(const T &stu);
template<typename T> void func4(T a);
template<typename T> void func5(T &a);

int name[20];
Student stu1("张华", 20, 96.5);  //创建一个Student类型的对象
func1(12.5, 30);  //Error
func2(name);  //name的类型从 int [20] 换转换为 int *,所以 T 的真实类型为 int
func3(stu1);  //非const转换为const,T 的真实类型为 Student
func4(name);  //name的类型从 int [20] 换转换为 int *,所以 T 的真实类型为 int *
func5(name);  //name的类型依然为 int [20],不会转换为 int *,所以 T 的真实类型为 int [20]

可以发现,当函数形参是引用类型时,数组不会转换为指针。

template<typename T> void func(T &a, T &b);

int str1[20];
int str2[10];
func(str1, str2);

  由于str1、str2的类型分别为int [20]和int [10],在函数调用过程中又不会转换为指针,所以编译器不知道应该将T实例化为int [20]还是int [10],导致调用失败

为函数模板显式地指明实参

  下面是一个实参推断失败的例子:

template<typename T1, typename T2> void func(T1 a){
    T2 b;
}
func(10);  //函数调用

func()有两个类型参数,分别是T1和T2,但是编译器只能推断出T1,不能推断出T2,调用失败

  显示指明的模板实参会按照从左到右的顺序与对应的模板参数匹配:第一个实参与第一个模板参数匹配,第二个实参与第二个模板参数匹配,只有尾部(最右)的类型参数的实参可以省略,而且前提是它们可以从传递给函数的实参中推断出来。 例如:

template<typename T1, typename T2> void func(T2 a){
    T1 b;
}
//函数调用
func<int>(10);  //省略 T2 的类型
func<int, int>(20);  //指明 T1、T2 的类型

显式的指明实参时可以应用正常的类型转换

  函数模板仅能进行[ const 转换 ]和[ 数组或函数指针转换 ]两种形式的类型转换,但是当我们显式的指明类型参数的实参时,就可以使用正常的类型转换了。例如:

template<typename T> void func(T a, T b);
func(10, 23.5);  //Error
func<float>(20, 93.7);  //Correct

  在第二种调用形式中,我们已经显式地指明了T的类型为float,编译器不会再为T的类型到底是int还是double而纠结了,所以可以从容地使用正常地类型转换了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值