C++函数匹配机制

函数匹配

在大多数情况下,我们容易确定某次调用应该选用哪个重载函数。

然而,当几个重载函数的形参数量相等以及某些形参的类型可以由其他类型转换得来时,这项工作就不那么容易了。

以下面这组函数及其调用为例:

void f();
void f(int);
void f(int, int);
void f(double, double = 3.14);
f(5.6); // 调用 void f(double, double)


确定候选函数和可行函数

候选函数

函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。

候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见

在这个例子中,有4个名为f的候选函数。

void f();
void f(int);
void f(int, int);
void f(double, double = 3.14);

可行函数

第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。

可行函数也有两个特征:

  • 一是其形参数量与本次调用提供的实参数量相等,
  • 二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。

我们能根据实参的数量从候选函数中排除掉两个。

不使用形参的函数和使用两个int形参的函数显然都不适合本次调用,这是因为我们的调用只提供了一个实参,而它们分别有0个和两个形参。

使用一个int 形参的函数和使用两个double形参的函数是可行的,它们都能用一个实参调用,其中最后那个函数本应该接受两个double值,但能因为它各有一个默认实参,所以只用一个实参也能调用它。

如果函教含有默认实参,则我们在调用该画就明体Note 入的实参数量可能少于它实际使用的实参数量。

在使用实参数量初步判别了候选函数后,接下来考察实参的类型是否与形参匹配。

和一般的函数调用类似,实参与形参匹配的含义可能是它们具有相同的类型,也可能是实参和形参类型满足转换规则。

在上面的例子中,剩下的两个函数都是可行的:

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

可行函数就只有

void f(int);
void f(double, double = 3.14);

如果没找到可行函数,编译器将报告无匹配函数的错误。

寻找最佳匹配(如果有的话)

函数匹配的第三步是从可行函数中选择与本次调用最匹配的函数。

在这一过程中,逐检查函数调用提供的实参,寻找形参类型与实参类型最匹配的那个可行函数。

下一节将介绍“最匹配”的细节,它的基本思想是,实参类型与形参类型越接近,它们匹配得越好。

在我们的例子中,调用只提供了一个(显式的)实参,它的类型是double。

如果调用f(int),实参将不得不从double转换成int。

另一个可行函数f(double,double)则与实参精确匹配。

精确匹配比需要类型转换的匹配更好,因此,编译器把f(5.6)解析成对含有两个double形参的函数的调用,并使用默认值填补我们未提供的第二个实参。

我们可以验证一下

#include<iostream>
using namespace std;
void f(){}
void f(int){}
void f(int, int){}
void f(double, double = 3.14) { cout << "调用f(double,double)版本" << endl; }

int main()
{
	f(5.6); // 调用 void f(double, double)
}

 

含有多个形参的函数匹配

当实参的数量有两个或更多时,函数匹配就比较复杂了。

对于前面那些名为f的函数,我们来分析如下的调用会发生什么情况:

f(42,2.56);

选择可行函数的方法和只有一个实参时一样,编译器选择那些形参数量满足要求且实参类型和形参类型能够匹配的函数。

此例中,可行函数包括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)更好。

编译器最终将因为这个调用具有三义性而拒绝其请求:因为每个可行函数各自在一个实参上实现了更好的匹配,从整体上无法判断哪个更好。

看起来我们似乎可以通过强制类型转换其中的一个实参来实现函数的匹配,但是在设计良好的系统中,不应该对实参进行强制类型转换。

调用重载函数时应尽量避免强制类型转换。如果在实际应用中确实需要强制类型转换,则说明我们设计的形参集合不合理。

实参类型转换

为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下所示:

1. 精确匹配,包括以下情况:

  • 实参类型和形参类型相同。
  • 实参从数组类型或函数类型转换成对应的指针类型。
  • 向实参添加顶层const或者从实参中删除顶层const。

2. 通过const转换实现的匹配。
3.通过类型提升实现的匹配。
4.通过算术类型转换或指针转换实现的匹配。
5.通过类类型转换实现的匹配。

需要类型提升和算术类型转换的匹配

内置类型的提升和转换可能在函数匹配时产生意想不到的结果,但幸运的是,在设计良好的系统中函数很少会含有与下面例子类似的形参。

分析函数调用前,我们应该知道小整型一般都会提升到int类型或更大的整数类型。

假设有两个函数,一个int、另一个接受short,则只有当调用提供的是short类型的值时才会选择short版本的函数。有时候,即使实参是一个很小的整数值,也会直接将它提升成int类型:此时使用short版本反而会导致类型转换:

void ff(int);
void ff(short);
ff('a');// 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,则当调用发生时编译器通过实参是否是常量来决定选择哪个函数:

#include<iostream>
using namespace std;

void lookup(int&) {
	cout << "调用int&版本" << endl;
}
void lookup(const int&) {
	cout << "调用const int&版本" << endl;
}


//调用1ookup (Account&)
int main()
{
	const int a = 0;
	int b;
	lookup(a);
	lookup(b);// 调用 lookup(const Account&)
}

 

在第一个调用中,我们传入的是const对象a。

因为不能把普通引用绑定到const对象上,所以此例中唯一可行的函数是以常量引用作为形参的那个函数,并且调用该函数与实参a精确匹配

在第二个调用中,我们传入的是非常量对象b。

对于这个调用来说,两个函数都是可行的,因为我们既可以使用b初始化常量引用也可以用它初始化非常量引用。然而,用非常量对象初始化常量引用需要类型转换,接受非常量形参的版本则与b精确匹配。因此应该选用非常量版本的函数。

指针类型的形参也类似。如果两个函数的唯一区别是它的指针形参指向常量或非常量,则编译器能通过实参是否是常量决定选用哪个函数:

如果实参是指向常量的指针,调用实参是const*的函数;如果实参是指向非常量的指针,调用形参是普通指针的函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513822.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【介绍什么是DDOS】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

ShareX 截图工具详细使用心得

一、软件介绍 ShareX是一款免费的开源程序。不仅可以截图&#xff0c;还可以录屏&#xff0c;自动添加水印和阴影&#xff0c;除此之外&#xff0c;还有很多很多&#xff0c;比如OCR识别、屏幕录制、颜色拾取、哈希检查、修改DNS、尺子功能、显示器测试等等。 二、下载安装 …

C++——list类及其模拟实现

前言&#xff1a;这篇文章我们继续进行C容器类的分享——list&#xff0c;也就是数据结构中的链表&#xff0c;而且是带头双向循环链表。 一.基本框架 namespace Mylist {template<class T>//定义节点struct ListNode{ListNode<T>* _next;ListNode<T>* _pre…

Stable Diffusion扩散模型推导公式的基础知识

文章目录 1、独立事件的条件概率2、贝叶斯公式、先验概率、后验概率、似然、证据3、马尔可夫链4、正态分布 / 高斯分布5、重参数化技巧6、期望7、KL散度 、高斯分布的KL散度8、极大似然估计9、ELBO :Evidence Lower Bound10、一元二次方程 1、独立事件的条件概率 A 和 B 是两个…

Rredis缓存常见面试题

文章目录 1.什么是缓存穿透&#xff0c;怎么解决2.什么是缓存击穿&#xff0c;怎么解决3.什么是缓存雪崩&#xff0c;怎么解决4.双写一致性问题5.redisson添加的排他锁是如何保证读写、读读互斥的6.为什么不使用延迟双删7.redis做为缓存&#xff0c;数据的持久化是怎么做的8.re…

【热门话题】文言一心与ChatGPT-4:一场跨时代智能对话系统的深度比较

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 文言一心与ChatGPT-4&#xff1a;一场跨时代智能对话系统的深度比较一、技术背景…

成绩管理系统|基于springboot成绩管理系统的设计与实现(附项目源码+论文)

基于springboot成绩管理系统的设计与实现 一、摘要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装毕业设计成绩管…

HarmonyOS应用开发ArkUI(TS)电商项目实战

项目介绍 本项目基于 HarmonyOS 的ArkUI框架TS扩展的声明式开发范式&#xff0c;关于语法和概念直接看官网官方文档地址&#xff1a;基于TS扩展的声明式开发范式&#xff0c; 工具版本&#xff1a; DevEco Studio 3.1 Canary1 SDK版本&#xff1a; 3.1.9.7&#xff08;API V…

海外媒体软文发稿:带动海外宣发新潮流,迈向国际舞台

引言 随着全球化的发展&#xff0c;越来越多的中国企业希望在国际舞台上展示自己的实力。而海外媒体软文发稿作为一种全新的海外宣传方式&#xff0c;正逐渐成为带动海外宣发新潮流的有力工具。本文将探讨海外媒体软文发稿的优势和如何迈向国际舞台。 海外媒体软文发稿的优势…

代码随想录阅读笔记-二叉树【最大二叉树】

题目 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树&#x…

linux系统负载对系统的意义

负载平均值的含义 负载平均值是通过uptime和top命令显示的三个数字&#xff0c;分别代表不同时间段的平均负载&#xff08;1分钟、5分钟和15分钟的平均值&#xff09;。这三个数字越低越好&#xff0c;较高的数字意味着系统可能存在问题或过载。然而&#xff0c;并没有一个固定…

男生穿什么裤子最帅?必备的男生裤子推荐

每个人都想拥有很多条好看质量又好的裤子。不过市面上有太多服装品牌&#xff0c;甚至还有不少劣质的衣裤&#xff0c;穿洗两遍之后就出现松垮、变形的情况。为了能够让大家可以选到合适的衣裤&#xff0c;我自费购买了多个品牌的裤子&#xff0c;并给出大家测评结果。 购买到质…

网站访问502,网站服务器崩溃,比较常见几个的原因

其实&#xff0c;配置再好的服务器也难免在使用过程中出现一些故障&#xff0c;造成宕机。 服务器一旦出现故障&#xff0c;影响到用户实时访问网站&#xff0c;造成用户流失&#xff0c;如果在企业的销售高峰期&#xff0c;则将直接影响到商业利润&#xff0c;而且不仅影响外…

SD-WAN降低网络运维难度的三大关键技术

企业网络作为现代企业不可或缺的基础设施&#xff0c;承担着连接全球的重要任务。随着全球化和数字化转型的加速推进&#xff0c;企业面临着越来越多的网络挑战和压力。传统的网络组网方式已经不能满足企业规模扩大、分支机构增多、上云服务等需求&#xff0c;导致了网络性能下…

消除歧义:利用动态上下文提出有效的RAG问题建议

原文地址&#xff1a;disambiguation-using-dynamic-context-in-crafting-effective-rag-question-suggestions 2024 年 4 月 3 日 这一策略唤起了IBM沃森率先采用的一项技术&#xff1a;消除歧义。面对用户模糊不清的输入&#xff0c;系统会提供大约五个或更少的选项供用户挑…

软件架构风格_3.以数据为中心的体系结构风格

以数据为中心的体系结构风格主要包括仓库体系结构风格和黑板体系结构风格。 1.仓库体系结构风格 仓库&#xff08;Repository&#xff09;是存储和维护数据的中心场所。在仓库风格&#xff08;见图1&#xff09;中&#xff0c;有两种不同的构件&#xff1a;中央数据结构说明当…

5米分辨率数字高程模型(DEM)的制作

在现代科技的驱动下&#xff0c;地理信息系统&#xff08;GIS&#xff09;和遥感技术已经取得了惊人的进展。其中一项令人瞩目的技术就是5米分辨率数字高程模型&#xff08;DEM&#xff09;的制作&#xff0c;它是基于多颗高分辨率卫星数据为原始数据&#xff0c;借助智能立体模…

Android 性能优化之黑科技开道(一)

1. 缘起 在开发电视版智家 App9.0 项目的时候&#xff0c;发现了一个性能问题。电视系统原本剩余的可用资源就少&#xff0c;而随着 9.0 功能的进一步增多&#xff0c;特别是门铃、门锁、多路视频同屏监控后等功能的增加&#xff0c;开始出现了卡顿情况。 经过调研分析发现有…

Apache DolphinScheduler 【安装部署】

前言 今天来学习一下 DolphinScheduler &#xff0c;这是一个任务调度工具&#xff0c;现在用的比较火爆。 1、安装部署 1.0、准备工作 1.0.1、集群规划 dolphinscheduler 比较吃内存&#xff0c;所以尽量给 master 节点多分配一点内存&#xff0c;桌面和虚拟机里能关的应用…

P2249 【深基13.例1】查找 (二分)

题目链接 代码&#xff1a; #include<algorithm> #include<iostream> #include<cstring> #include<queue> #include<cmath>using namespace std; //就是找左端点&#xff0c;没有输出-1 int n; int q; int a[1000010];int main() {scanf("…