文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

深入了解C++函数重载解析策略

2022-11-13 18:36

关注

参考《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,张海龙 袁国忠译,人民邮电出版社。C++ 使用重载解析策略来决定为函数调用使用哪一个函数定义。重载解析过程大致分为如下三步:

第 1 步:创建候选函数列表,只要求函数名一样即可,对函数特征标以及是否为模板函数无要求;

第 2 步:在上一步的基础上创建可行函数列表,包含特征标完全匹配的常规函数或模板函数、以及实参隐式转换后完全匹配的常规函数或模板函数,这些都是参数数目正确的函数;

第 3 步:在上一步的基础上确定最佳匹配函数,若有则使用它,若没有则该函数调用失败。

下面以一个例子来说明这个重载过程:

//全部函数原型
void may(int);                        //原型#1
float may(float, float = 3);          //原型#2
void may(char);                       //原型#3
char * may(const char *);             //原型#4
char may(const char &);               //原型#5
template<class T> void may(const T &);//原型#6
template<class T> void may(T *);      //原型#7
void may(char, double);               //原型#8
void mbk(float);                      //原型#9
char mkk(int, char);                  //原型#10
int mck(char);                        //原型#11
double myk(float);                    //原型#12
void mpk(char);                       //原型#13
 
//函数调用
may('B');
 
//函数定义
...

重载第 1 步:创建候选函数列表。即函数名称为 may 的常规函数和模板函数,候选函数列表如下:

//重载第1步:创建候选函数列表
void may(int);                        //原型#1
float may(float, float = 3);          //原型#2
void may(char);                       //原型#3
char * may(const char *);             //原型#4
char may(const char &);               //原型#5
template<class T> void may(const T &);//原型#6
template<class T> void may(T *);      //原型#7
void may(char, double);               //原型#8

重载第 2 步:创建可行函数列表。由于整数类型 char 不能被隐式地转换为指针类型 char *,因此函数 #4 和函数 #7 都被排除,而函数 #8 因为参数数目不匹配也会被排除。进行完全匹配时,C++ 允许下表这些无关紧要的转换,表中 Type 表示任意类型,例如 char & 到 const char & 的转换也包含在内,表中 Type (argument-list) 意味着用作实参的函数名和用作形参的函数指针只要返回类型和参数列表相同,就是匹配的。

实参类型形参类型
TypeType &
Type &Type
Type []Type *
Type (argument-list)Type (*) (argument-list)
Typeconst Type
Typevolatile Type
Type *const Type *
Type *volatile Type *

根据此表可知,剩下的函数中包含特征标完全匹配的常规函数 #3 和 #5、特征标完全匹配的模板函数 #6(此时 T 可以被实例化为 char)、实参隐式转换后完全匹配的常规函数 #1 和 #2。可行函数列表如下:

//重载第2步:创建可行函数列表
void may(int);                        //原型#1
float may(float, float = 3);          //原型#2
void may(char);                       //原型#3
char may(const char &);               //原型#5
template<class T> void may(const T &);//原型#6

重载第 3 步:确定最佳匹配函数。通常,从最佳到最差的顺序如下所述:

依此规则,函数 #3 和函数 #5、函数 #6 都是特征标完全匹配的最佳匹配函数,函数 #1 需经隐式提升转换,函数 #2 需经隐式标准转换,由此各函数最佳匹配程度为:(#3, #5, #6) > #1 > #2。当特征标完全匹配时,又有如下规则:

依此规则,非模板函数 #3 和 #5 最佳匹配程度要高于模板函数 #6 ,即各函数最佳匹配程度为:(#3, #5) > #6 > #1 > #2。最终出现了两个最佳匹配函数 #3 和 #5 ,因此该函数调用失败,编译器将报错。

//重载第 3 步:确定最佳匹配函数
void may(char);                       //原型#3
char may(const char &);               //原型#5

下面展开来说上述几条完全匹配时的规则。

第 1 条:指向非 const 数据的指针和引用优先与形参为非 const 指针和引用的函数匹配,这一点需明确,const 和非 const 之间的区别只适用于指针和引用。下面 4 个函数都与函数调用是完全匹配的:

//函数原型
void recycle(int);        //原型#1
void recycle(const int);  //原型#2
void recycle(int &);      //原型#3
void recycle(const int &);//原型#4
 
//函数调用
int x = 5;
recycle(x);
 
//函数定义
...

第 2 条:优先与非模板函数匹配,这一点比较简单,当完全匹配的函数中,一个是非模板函数,另一个是模板函数时,非模板函数将优于模板函数,显式具体化、显式实例化、隐式实例化都属于模板函数。

第 3 条:同为模板函数时,优先与较具体的模板函数匹配,找出最具体的模板的规则被称为函数模板的部分排序规则(partial ordering rules)。这意味着显式具体化优先于常规模板函数,都为常规模板函数时,编译器优先选择实例化时类型转换更少的那一个。以下面的程序为例,调用方式 recycle(&ink) 既与模板 #1 匹配,此时 Type 将被解释为 blot *,也与模板 #2 匹配,此时 Type 将被解释为 blot,因此将这两个隐式实例 recycle<blot *>(blot *) 和 recycle<blot>(blot *) 发送到可行函数池中。在选择最佳匹配函数时,#2 被认为是更具体的,因为它已经显式地指出,函数参数是指向 Type 的指针,相比于 #1,它对类型的要求更加地具体,在生成过程中所需要的转换更少,因此调用方式 recycle(&ink) 实际会匹配版本 #2。

//两个常规模板函数
template <class Type> void recycle(Type t);   //原型#1
template <class Type> void recycle(Type * t); //原型#2
 
//调用程序包含如下代码
struct blot {int a; char b[10];};
blot ink = {25, "spots"};
...
recycle(&ink);  //使用版本#2
 
//函数定义
...

部分排序规则的另一个示例程序如下,它与上一个例子有异曲同工之妙。由于模板 #2 做了特定的假设:数组内容是指针,对类型的要求更加地具体,因此在调用时第一个参数若传入指针数组 pt,则将实际匹配函数 #2。

//两个常规模板函数
template <typename T> 
void ShowArray(T arr[], int n);   //原型#1
template <typename T> 
void ShowArray(T * arr[], int n); //原型#2
 
//调用程序包含如下代码
int things[6] = {13, 31, 103, 301, 310, 130};
int * pt[3] = {&things[0], &things[2], &things[4]};
ShowArray(things, 6);  //使用版本#1
ShowArray(pt, 3);      //使用版本#2
 
//函数定义
...

将有多个参数的函数调用与有多个参数的原型进行匹配时,编译器必须考虑所有参数的匹配情况。如果找到比其他可行函数都合适的函数,则选择该函数。一个函数要比其他函数都合适,其所有参数的匹配程度都必须不比其他函数差,同时至少有一个参数的匹配程度比其他函数都高。

在有些情况下,可通过编写合适的函数调用,来引导编译器做出程序员期望的选择。如下所示,其中模板函数返回两个值中较小的一个,非模板函数返回两个值中绝对值较小的那个。第一次调用时根据重载解析策略选择了非模板函数 #2;第二次调用时根据重载解析策略选择了模板函数 #1 的 double 版本,属于模板函数的隐式实例化;第三次调用的 <> 指出,编译器应该选择模板函数,此时编译器会查看调用函数时的实参类型来进行实例化,也属于模板函数的隐式实例化;第四次调用的 <int> 显式指出,编译器应该使用模板函数的 int 实例化版本,此时属于模板函数的显式实例化。

#include <iostream>
 
//函数#1
template<class T>
T lesser(T a, T b)
{
    return a < b ? a : b;
}
 
//函数#2
int lesser(int a, int b)
{
    a = a < 0 ? -a : a;
    b = b < 0 ? -b : b;
    return a < b ? a : b;
}
 
//函数调用
int main()
{
    using namespace std;
    
    int m = 20;
    int n = -30;
    double x = 15.5;
    double y = 25.9;
    
    //使用#2,结果为20
    cout << lesser(m, n) << endl;
    
    //使用#1,double隐式实例化,结果为15.5
    cout << lesser(x, y) << endl;
    
    //使用#1,int隐式实例化,结果为-30
    cout << lesser<>(m, n) << endl;
    
    //使用#1,int显式实例化,结果为15
    cout << lesser<int>(x, y) << endl;
    
    return 0;
}

到此这篇关于深入了解C++函数重载解析策略的文章就介绍到这了,更多相关C++函数重载解析内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯