概述
模板是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++有一套定义好的策略,来决定为函数调用选择哪一个函数定义。这个函数选择的过程,称为重载解析。以下是重载解析的过程:
- 创建候选函数列表。其中包含所有与被调函数的名称相同的函数和模板函数。
- 使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中可能包含实参与形参的类型完全匹配的情况。
- 确定是否由最佳可行函数,如果有则使用它,否则函数调用出错。
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更具体
最后
以上就是正直短靴为你收集整理的C++函数的模板与重载解析的全部内容,希望文章能够帮你解决C++函数的模板与重载解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复