C++基本概念——聊聊C++中的函数匹配那些事儿

为什么会有函数匹配?

    在大多数情况下,我们容易确定某次调用应该选择哪个重载函数,但是当重载函数的形参数目相等以及某些形参可以由其他类型转换得来的时,我们就需要明白函数匹配的原理。

函数匹配的过程

    要理解函数匹配的过程,我们还是通过一个例子来进行解释,以下面这组调用函数为例:

void f();
②void f(int);
③void f(int,int);
④void f(double,double=3.14);
f(5.6); //调用void f(double,double)
  1. 确定对应的重载函数集,集合中的函数叫做候选函数。所谓的候选函数具备两个特征:一是与被调函数同名,二是在声明调用点可见。在这个例子中有四个候选函数。
  2. 通过本次调用的实参选择候选函数中能被调用的函数,这样的函数我们称之为可行函数。同样的,我们的可行函数也有两个特征:一是其形参的数量与本次调用的实参的数量相等,二是每个实参的类型与对应的形参类型相同,或者能够转换为相同的类型。如果没有可行函数,编译器会怎么样呢?当然是报告无匹配函数的错误啦。
  3. 最后一步就是从可行函数中挑选最佳的匹配函数。怎么确定是最佳匹配呢?有且只有一个函数满足下列条件,则是最佳匹配函数:
    • 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
    • 至少有一个行参的匹配优于其他可行函数提供的匹配。

    本例中首先根据实参的数量排除①和③两个函数,然后我们得到可行函数集②和④;在使用形参的数量初步判别了候选函数后,接下来考察实参的类型是否与形参匹配,原则上这两个函数都是可行的:

  • f(int)是可行的,因为实参的类型double能够转换为int
  • f(double,double)是可行的的,是因为它的第二个形参提供了默认值,而第一个形参的类型恰好是double,与函数使用的实参类型完全一致。

    根据上面的步骤,最后一步当然是寻找最匹配的函数,它的基本的思想是实参类型与形参类型越接近,他们匹配的越好。在这个例子中,②需要强制类型转换,而④是精确匹配,因此编译器将会调用函数④。

含有多个形参的函数匹配

    我们继续在前面例子的基础上进行分析,如果调用函数为下面的形式:

f(42,5.6);

    对于这个调用的可行函数是③和④,③和④在此处是二义性调用。为什么会是这样呢?其实,也不难理解。如果调用函数③,那么第二参数要从double转换为int。如果调用函数④,那么第一参数要从int转换为double。显然,这两种情况下的转换代价是相当的,编译器无法分辨出最优的匹配,报告二义性调用错误。

如何确定最佳匹配?

    从前面的讨论中我们可以看出,确定函数最佳匹配的过程并不是特别容易,因为在这个过程中往往会涉及到参数类型转换的问题。实际中,参数转换当然不会只有doubleint之间的转换,编译器为了对这些转换进行区分将实参到行参的类型转换区分为以下几个等级:


(1)精确匹配
- 实参类型和行参类型相同
- 实参从数组类型或者函数类型转换为对应的指针类型
- 向实参中添加顶层cosnt或者从实参中删除顶层const,关于顶层const和顶层cosnt的概念,详见博客。举个例子,

int i=0;
f(int *const a){} 
void main(){
    f(&i);//这里&i本来的类型是int*,函数调用给原本的int *添加const,
    //也就是说添加了在函数内部不能改变指针值的限制,这样的改变不会导致任何损失
} 


(2) 通过const转换实现的匹配

int i=0;
f(const int *a){} 
void main(){
    f(&i);//这里函数的形参味指向const int的指针,但是传入的实参是一个
    //指向int的指针,这里不能通过指针修改i的值
} 

(3)通过类型提升实现的匹配,关于类型转化不清楚的可以参见我的另一篇博客
(4)通过算术类型转换或者是指针转换实现的匹配
(5) 通过类类型转化实现的匹配

    规则虽然简单,但是要长久记忆仍然是一项颇具挑战性的工作。常言道知其然,更知其所以然,因此我们就探讨以下C++语言的设计者是基于什么样的考虑才制定了这样的标准。
    简而言之,这种分类标准实际上是基于两条主线:第一条是是否会在匹配过程中存在精度损失,第二条是语言内置类型之间的转换要优于用户自定义类型的转换,毕竟用户自定义类型是基于内置类型的嘛。我们看一下这两条主线在这个规则中是如何进行体现的。首先,精确匹配当然是最理想的状态,这一点毋庸置疑。接着,const转换当然也没有任何精度上的损失,但是给原有的数据类型的使用增加了限制。类型提升导致数据原有精度发生了变化,但是并未损失。通过算术类型转换或者指针类型转换进行匹配,这样的转换导致精度的损失。最后是用户自定义类型的转换,遵循第二条主线。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
函数指针是指向函数的指针,它可以用来执行函数的操作。函数指针的基本概念是它的声明格式为type (*ptrname)(形参列表)。其,type是函数返回值的类型,ptrname是函数指针的名称,形参列表是函数的参数类型。通过使用函数指针,我们可以将函数或者函数指针作为某一个函数的形式参数传入并使用,例如在C++11的thread头文件,线程的构造函数需要传递一个函数指针的实例。通过使用函数指针,我们可以实现函数的动态调用,灵活地在程序运行时决定要调用的函数。因此,函数指针在C语言和C++都是非常重要的概念和工具。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [c++函数指针](https://blog.csdn.net/weixin_50866517/article/details/113247374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++函数指针用法](https://blog.csdn.net/qq_48201696/article/details/122335138)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值