定义
作用于同一作用域内的几个函数名字相同但是形参列表不同,我们称之为重载函数。
规则
1.对于重载函数来说,应该在形参数量或形参类型上有所不同。
2.不允许两个函数除了返回类型外其他的要素都相同
3.一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。另一方面,如果形参是某种类型的指针或引用,则通过其指向的是常量对象还是非常量对象可以实现重载,此时const是底层的。
编译器可以通过实参是否是常量来推断应该调用哪个函数。因为const不能转化为其他类型,而非常量可以转化为const类型。当有一个常量版本和一个非常量版本,我们传递一个const只能用常量版本,而传递一个非常量时,编译器会优先选择非常量版本的函数。
const_cast和重载
举个栗子:
// 比较两个string对象的长度,返回较短的那个引用
const string &shorterString(const string &s1,const string &s2)
{
return s1.size()<=s2.size() ? s1 : s2;
}
string &shorterString(string &s1,string &s2)
{
auto &r=shorterString(const_cast<const string &>(s1),
const_cast<const string &>(s2));
return const_cast<string &>(r);
}
这里的一些细节:
1.const_cast加上和去除底层const
2.在非常量的版本中调用常量的版本,得到的返回是常量的,而本身s1,s2是非常量的,所以去掉r的底层const是没有关系的
调用重载的函数
现在需要掌握的三种情况(可能书后边也有多讲吧):
1.编译器找到一个与实参最佳匹配的函数,生成调用该函数的代码
2.找不到任何一个函数与调用的实参匹配,编译器发出无匹配的错误信息
3.有多于一个函数可以匹配,但是每一个都不是最佳选择,此时也将发生错误,称为二义性调用(ambiguous call)
重载与作用域
一旦在作用域中找到了所需的名字,编译器就会忽略外层作用域中的同名实体,剩下的工作就是检查函数调用是否有效了(P210)
函数匹配
具体见原书P217
以下面这组函数及其调用为例:
void f();
void f(int);
void f(int,int);
void f(double,double=3.14);
f(5.6);//调用void f(double,double)
确定候选函数和可行函数
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。在这个例子中,有四个名为f的候选函数。
第二步考察本次调用中的形参,然后从调用函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。可行函数有两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。
实参数量判断:
我们能根据实参的数量排除两个。不使用形参的函数和使用两个int的函数。
使用一个int形参的函数和使用两个double形参的函数是可行的,它们都能用一个实参调用。
实参类型判断:
显然,两个double形参的函数更加匹配
note:如果没有找到可行函数,编译器将报告无匹配函数的错误
寻找最佳匹配
函数匹配的第三步是从可行函数中选择与本次调用最匹配的函数。那么什么是最匹配呢,当然是指形参类型和实参类型最匹配的可行函数,关于最匹配的细节,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下所示:
1.精确匹配:
实参类型和形参类型相同
实参从数组类型或函数类型转换成对于的指针类型
向实参添加顶层const或删除顶层const
2.通过const转换实现的匹配
3.通过类型提升实现的匹配
4.通过算数类型或指针转换实现的匹配
5.通过类类型转换实现的匹配
含有多个形参的函数匹配
当实参的数量有两个或者更多时,函数匹配就比较复杂了。对于前面哪些名为f的函数,我们来分析(42,2.56);会发生什么情况。此例中f(int,int)和f(double,double)都是可行函数。接下来,编译器将依次检查每个实参确定哪个函数是最佳匹配。如果有且只有一个函数满足下列条件,则匹配成功:
1.该函数每个实参的匹配都不劣于其他可行函数需要的匹配
2.至少有一个实参的匹配优于其他可行函数提供的匹配
如果检查了所有形参后没有一个函数脱颖而出,那么该调用是错误的。编译器将报告二义性调用的信息。
f(int,int)在第一个实参上比f(double,double)更好,而f(double,double)在第二个实参上比f(int,int)更好,所以编译器最终因为这个调用具有二义性而拒绝其请求。