C++函数的模板与重载解析

模板是C++泛型编程的基础,一个模板就是一个创建类或者函数的方案。在具体使用时,需要将模板参数转化为具体类型。编译器会对函数模板进行两次编译,在声明的地方对模板本身进行编译,在调用的地方对参数替换后的代码进行编译。

C++提供两种模板机制:函数模板和类模板,这里我们只讨论函数模板。

1,实例化和具体化
为了更好的了解模板,我们必须理解术语实例化和具体化。模板是生成函数定义的方案,编译器使用模板为特定的类型生成函数定义时,得到模板的实例(具体类型的函数实现)。
1.1,实例化

首先我们来聊一聊实例化,实例化可以分为显式实例化和隐式实例化。不管是哪一种实例化方式,都需要编译器通过模板来生成定义。
显式实例化,由我们指定typename ;隐式实例化,由编译器自动推导typename。

tamplate <typename T>
void Swap(T &a,T &b)

//隐式实例化:
char a,b;
Swap(a,b);          //直接调用,a,b的类型并没有显式实例化声明
                //typename 由编译器自动推导
//显式实例化:
//1,声明阶段显式实例化:
tamplate Swap<int> (int,int);

int c,d;
Swap(c,d);
//2,调用阶段显式实例化:
Swap<int> (c,d);        //不做声明,直接通过Swap<int>指定类型

注意:
隐式实例化调用时,需要编译器自动推导typename 类型,所以参数不能进行隐式转换。显式实例化调用时,可以进行隐式转换,如果转换不成功,则无法调用模板。

1.2,具体化
具体化是为了解决模板函数面对自定义数据类型时候的局限性。
上文提到过,不管是哪一种实例化方式,都需要编译器通过模板来生成定义。但是具体化,需要我们给出函数的定义
一般情况下,C++基本数据类型(int,char)都可以采用显式或隐式实例化调用;自定义数据类型(class,struct)需要做出显式具体化,而不是由编译器根据模板生成定义。

//以下两种方式皆可:
template <> void Swap(job &a, job &b);
template <> void Swap<job>(job &a, job &b);

struct job
{
    string name;
    int age;
    job(string _name,int age):name(_name),age(age){};
};
//显式具体化,给出函数定义。
template <> void Swap(job &a, job &b)
{
    int temp;
    temp = a.age;
    a.age= b.age;
    b.age= temp;
}
//调用时:
Swap(job1,job2);

第三代具体化(ISO/ANXI C++标准):

  • 对于给定的函数名,可以有非模板函数,模板函数和具体化模板函数,以及他们的重载版本
  • 显式具体化的原型和定义应以template<>开头,并通过名称指定类型
  • 非模板函数 > 具体化模板函数 > 常规模板函数(调用优先级)

2,解析策略
对于函数重载,函数模板,函数模板重载,C++有一套定义好的策略,来决定为函数调用选择哪一个函数定义。这个函数选择的过程,称为重载解析。以下是重载解析的过程:

  1. 创建候选函数列表。其中包含所有与被调函数的名称相同的函数和模板函数。
  2. 使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中可能包含实参与形参的类型完全匹配的情况。
  3. 确定是否由最佳可行函数,如果有则使用它,否则函数调用出错。
void may(int);                      //#1
float may(float,float = 0)          //#2
void may(char)                      //#3
char * may(const char *)            //#4
char may(const char &)              //#5
template<typename T> void may(const T &)//#6
template<typename T> void may(T *)   //#5

//函数调用如下:
may('a');

执行重载解析:
1,根据上边的候选函数列表,#4和#7不可行,整形不能隐式转换为指针类型
2,其余的函数,都可以被使用(如果它是声明的唯一一个函数)
3,确定哪个函数是最佳函数(查看转换)

  • 完全匹配(常规函数优于模板)
  • 提升转换(安全转换:char,short转int)
  • 标准转换(不安全转换:int 转char,short)
  • 用户自定义的转换,如类中声明的转换

4,#1优于#2,char=>int(提升转换),char=>float(标准转换)
5,#3,#5,#6 都优于#1,#2,因为他们都是完全匹配的。
6,#3,#5由于#6,因为#6是模板函数
7,此时#3,#5都完全匹配,此时可能出现二义性
注意:完全匹配和最佳匹配:
进行完全匹配时,C++允许一些无关紧要的转换。
这里写图片描述

有时候两个函数都完全匹配,但是仍可能完成重载解析
1,指向 非const的指针和引用优先 与 非const的指针和引用的形参原型函数相匹配。const与const优先匹配
2,非模板函数优先于模板函数
3,如果两个都是模板函数,较具体的优先
术语:最具体:
编译器推断使用哪一种函数原型,需要执行的转换最少。
例:

template <typename T> void Recycle(T t);    #1
template <typename T> void Recycle(T* t);   #2

//模板调用
Rec rec;  //自定义类型
Recycle( &rec)
#1 T需要转换为 Rec *
#2 T需要转换为 Rec
所以说#2更具体
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值