前言
当几个重载的函数形参数量相等以及某些形参的类型可以由其他类型转换得来时,我们就不太容易确定某次调用应该选用哪个重载函数。
以下面这组函数及其调用为例:
void f();
void f(int);
void f(int,int);
void f(double,double=3.14);
f(5.6); //调用void f(double,double)
一、确定候选函数和可行函数
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。
候选函数具备两个特征:
1.与被调用的函数同名。
2.声明在调用点可见。
在这个例子中,有个4个名为f的候选函数。
第二步考擦本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。
可行函数具备两个特征:
1.形参数量与本次调用提供的实参数量相等。
2.每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。
如果函数含有默认实参,则在外面调用该函数时传入的实参数量可能少于它实际使用的实参数量。
二、实参类型转换
为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下所示:
一.精确匹配,包括以下情况:
1.实参类型和形参类型相同。
2.实参从数组类型或函数类型转换成对应的指针类型。
3.向实参添加顶层const或者从实参中删除顶层const。
二.通过const转换实现的匹配。
三.通过类型提升实现的匹配。
四.通过算术类型转换或指针转换实现的匹配。
五.通过类类型转换实现的匹配。
1.需要类型提升和算术类型转换的匹配
假设有两个函数,一个接受int、另一个接受short,则只有当调用提供的是short类型的值才会选择short版本的函数。有时候,即使实参是一个很小的整数值,也会将它提升成int类型;此时使用short版本反而会导致类型转换:
void ff(int);
void ff(short);
ff('a'); //char提升成int;调用f(int)
所有算术类型转换的级别都一样。例如:
void mainp(long);
void mainp(float);
mainp(3.14); //错误:二义性调用
字面值3.14的类型是double,它既能转换成long也能转换成float。因为存在两种可能的算术类型转换,所以该调用具有二义性。
2.函数匹配和const实参
重载函数的区别在于它们的引用类型的形参是否引用了const,或者指针类型的形参是否指向const,则当调用发生时编译器通过实参是否常量来决定选择哪个函数:
Record lookup(Acconut &); //函数的参数是Acconut引用
Record lookup(const Acconut &); //函数的参数是Acconut常量引用
const Account a;
Account b;
lookup(a); //调用lookup(const Acconut &)
lookup(b); //调用lookup(Acconut &)
第一个调用中,传入的是const对象a。因为不能把普通引用绑定到const对象上,所以唯一可行的函数是以常量引用作为形参的哪个函数,并且调用该函数与实参a精确匹配。