欢迎阅读我的 【C++Primer】专栏
专栏简介:本专栏主要面向C++初学者,解释C++的一些根本概念和根本语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中界说的抽象设施,使你更加顺应高级步伐设计技能。希望对读者有资助!
6.6函数匹配
在大多数情况下,我们轻易确定某次调用应该选用哪个重载函数。然而,当几个重载函数的形参数量相称以及树些形参的范例可以由其他范例转换得来时,这项工作就不那么轻易了。以下面这组函数及其调用为例:
- void f();
- void f(int);
- void f(int,int);
- void f(double,double=3.14);
- f(5.6);//调用void(double,double)
复制代码 确定候选函数和可行函数
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。候选函数具备两个特征:一是与被调用的函数合名,二是其声明在调用点可见。在这个例子中,有4个名为f的候选函数。
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数(variable function)。可行函数也有两个特征:一是其形参数量与本次调用提供的实参数量相称,二是每个实参的范例与对应的形参范例雷同,或者能转换成形参的范例。
我们能根据实参的数量从候选函数中排除掉两个。不使用形参的函数和使用两个int形参的函数显然都不适合本次调用,这是因为我们的调用只提供了一个实参,而它们分别有0个和两个形参。
使用一个int形参的函数和使用两个double形参的函数是可行的,它们都能用一个实参调用。其中最后谁人函数本应该接受两个double值,但是因为它含有一个默认实
参,以是只用一个实参也能调用它。
在使用实参数量开端判别了候选函数后,接下来考察实参的范例是否与形参匹配。和一般的函数调用雷同,实到场形参匹配的含义大概是它们具有雷同的范例,也大概是实参范例和形参范例满足转换规则。在上面的例子中,剩下的两个函数都是可行的:
- f(int)是可行的,因为实参范例double能转换成形参范例int。
- f(double,double)是可行的,因为它的第二个形参提供了默认值,而第一个形参的范例正好是double,与函数使用的实参范例完全同等。
如果没找到可行函数,编译器将陈诉无匹配函数的错误。
寻找最佳匹配(如果有的话)
函数匹配的第三步是从可行函数中选择与本次调用最匹配的函数。在这一过程中,逐一查抄函数调用提供的实参,寻找形参范例与实参范例最匹配的谁人可行函数。下一节将
介绍“最匹配“的细节,它的根本思想是,实参范例与形参范例越接近,它们匹配得越好。
在我们的例子中,调用只提供了一个(显式的实参,它的范例是double。如果调用f(int),实参将不得不从double转换成int。另一个可行函数f(double,double)则与实参精确匹配。精确匹配比需要范例转换的匹配更好,因此,编译器把f(5.6)剖析成对含有两个double形参的函数的调用,并使用默认值填补我们未提供的第二个实参。
含有多个形参的函数匹配
当实参的数量有两个或更多时,函数匹配就比力复杂了。对于前面那些名为f的函数,我们来分析如下的调用会发生什么情况:
选择可行函数的方法和只有一个实参时一样,编译器选择那些形参数量满足要求东实参范例和形参范例可以或许匹配的函数。此例中,可行函数包括f(int,int)和f(double,double)。接下来,编译器依次查抄每个实参以确定哪个函数是最佳匹配。如果有东只有一个函数满足下列条件,则匹配乐成:
- 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
- 至少有一个实参的匹配优于其他可行函数提供的匹配。
如果在查抄了所有实参之后没有任何一个函数脱颖而出,则该调用是错误的。编译器将陈诉二义性调用的信息。
在上面的调用中,只思量第一个实参时我们发现函数f(int,int)能精确匹配;要想匹配第二个函数,int范例的实参必须转换成double范例。显然需要内置范例转换的匹配劣于精确匹配,因此仅就第一个实参来说,f(int,int)比f(double,double)更好。
接着思量第二个实参2.56,此时f(double,double)是精确匹配;要想调用f(int,int)必须将2.56从double范例转换成int范例.因此仅就第二个实参来说,f(double,double)更好。
编译器最终将因为这个调用具有二义性而拒绝其请求:因为每个可行函数各安闲一个实参上实现了更好的匹配,从团体上无法判断孰优孰劣。看起来我们好像可以通过逼迫范例转换其中的一个实参来实现函数的匹配,但是在设计精良的系统中,不应该对实参进行逼迫范例转换。
调用重载函数时应尽量避免逼迫范例转换,如果在实际应用中确实需要逼迫范例转换,则说明我们设计的形参集合不公道:
实参范例转换
为了确定最佳匹配,编译器将实参范例到形参范例的转换划分成儿个等级,具体排序如下所示:
- 精确匹配,包括以下情况:
- 实参范例和形参范例雷同。
- 实参从数组范例或函数范例转换成对应的指针范例.
- 向实参添加顶层const或者从实参中删除顶层const。
- 通过const转换实现的匹配。
- 通过范例提升实现的匹配。
- 通过算术范例转换或指针转换实现的匹配。
- 通过类范例转换实现的匹配。
需要范例提升和算术范例转换的匹配
内置范例的提升和转换大概在函数匹配时产生意想不到的结栗,但幸运的是,在设计精良的系统中函数很少会含有与下面例子雷同的形参。
分析函数调用前,我们应该知道小整型一般都会提升到int范例或更大的整数范例。
假设有两个函数,一个接受int、另一个接受short,则只有当调用提供的是short范例的值时才会选择short版本的函数。偶然候,即使实参是一个很小的整数值,也会直接将它提升成int范例;此时使用short版本反而会导致范例转换:
- void ff(int);
- void ff(short);
- ff(7)//char提升成int;调用f(int)
复制代码 所有算术范例转换的级别都一样。例如,从int向unsigned int的转换并不比从int向double的转换级别高。举个具体点的例子,思量
- void manip(long);
- void manip(float);
- manip(3.14);//错误:二义性调用
复制代码 字面值3.14的范例是double,它既能转换成long也能转换成float。因为存在两种大概的算数范例转换,以是该调用具有二义性。
函数匹配和const实参
如果重载函数的区别在于它们的引用范例的形参是否引用了const,或者指针范例的形参是否指向const,则当调用发生时编详器通过实参是否是常量来决定选择哪个函数:
- Record lookup(Account&);//函数的参数是Account的引用
- Record lookup(const Account&);// 函数的参数是一个常量引用
- const Account a;
- Account b;
- lookup(a);//调用lookup(const Account&)
- lookup(b);//调用lookup(Account&)
复制代码 在第一个调用中,我们传入的是const对象a,因为不能把平常引用绑定到const对象上,以是此例中唯一可行的函数是以常量引用作为形参的谁人函数,并且调用该函数与实参a精确匹配。
在第二个调用中,我们传入的好坏常量对象b。对于这个调用米说,两个函数都是可行的,因为我们既可以使用b初始化常量引用也可以用它初始化非常量引用。然而,用非常量对象初始化常量引用需要范例转换,接受非常量形参的版本则与b精确匹配。因此,应该选用非常量版本的函数。
指针范例的形参也雷同。如果两个函数的唯一区别是它的指针形参指向常量或非常量,则编译器能通过实参是否是常量决定选用哪个函数,如果实参是指向常量的指针,调用形参是const*的函数;如果实参是指向非常量的指针,调用形参是平常指针的函数。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |