C++函数重载过程中的二义性和类型转换
来源:互联网 发布:scratch编程教程 pdf 编辑:程序博客网 时间:2024/06/02 19:41
【学习于C语言中文网,请勿转载】
发生函数调用时编译器会根据传入的实参的个数、类型、顺序等信息去匹配要调用的函数,这在大部分情况下都能够精确匹配。但当实参的类型和形参的类型不一致时情况就会变得稍微复杂,例如函数形参的类型是int
,调用函数时却将short
类型的数据交给了它,编译器就需要先将short
类型转换为int
类型才能匹配成功。
现在有以下几种形式的函数重载(例1):
- #include <iostream>
- using namespace std;
- //1号函数
- void func(char ch){
- cout<<"#1"<<endl;
- }
- //2号函数
- void func(int n){
- cout<<"#2"<<endl;
- }
- //3号函数
- void func(long m){
- cout<<"#3"<<endl;
- }
- //4号函数
- void func(double f){
- cout<<"#4"<<endl;
- }
- int main(){
- short s = 99;
- float f = 84.6;
- func('a'); //不需要类型转换,调用func(char)
- func(s); //将short转换成int,调用func(int)
- func(49); //不需要类型转换,调用func(int)
- func(f); //将float转换成double,调用func(double)
- return 0;
- }
#1
#2
#2
#4
void func(int n)
去掉(例2):
- #include <iostream>
- using namespace std;
- //1号函数
- void func(char ch){
- cout<<"#1"<<endl;
- }
- //3号函数
- void func(long m){
- cout<<"#3"<<endl;
- }
- //4号函数
- void func(double f){
- cout<<"#4"<<endl;
- }
- int main(){
- short s = 99;
- float f = 84.6;
- func('a');
- func(s);
- func(49);
- func(f);
- return 0;
- }
func(s)
和func(49)
这两个函数发生调用错误,它们可以匹配三个重载函数中的任何一个,编译器不知道如何抉择。这着实有点让人摸不着头脑!根据以往的编程经验,s 和 49 不都应该被转换成 long 类型,从而匹配3号函数
void func(long m)
吗?这种推论在一般的函数调用或者四则运算中确实没错,但它不一定适用于重载函数!C++ 标准规定,在进行重载决议时编译器应该按照下面的优先级顺序来处理实参的类型:
低->高:char->short、 char short int ->long
高->低:long int short->char、 long int ->short
小数转换从 double 到 float整数和小数转换char short int long -> float double、float double -> char short int long指针转换从 int * 到 void *。记住类型提升(bool char short ->int、float->double)就行了,自动类型转化类型较多。
C++ 标准还规定,编译器应该按照从高到低的顺序来搜索重载函数,首先是精确匹配,然后是类型提升,最后才是类型转换;一旦在某个优先级中找到唯一的一个重载函数就匹配成功,不再继续往下搜索。
如果在一个优先级中找到多个(两个以及以上)合适的重载函数,编译器就会陷入两难境地,不知道如何抉择,编译器会将这种模棱两可的函数调用视为一种错误,因为这些合适的重载函数同等“优秀”,没有一个脱颖而出,调用谁都一样。这就是函数重载过程中的二义性错误。
在例1中,
func('a')
、func(49)
分别和void func(char)
、void func(int)
精确匹配;func(s)
没有精确匹配的重载函数,编译器将 s 的类型提升为 int 后和void func(int)
匹配成功;func(f)
也是类似的道理,将 f 提升为 double 类型后和void func(double)
匹配成功。在例2中,
func(s)
、func(49)
没有精确匹配的重载函数,将它们的类型都提升为 int 后仍然不能匹配,接下来进入自动类型转换阶段,发现 s 被转换为 char(整型转换)、long(整型转换)、double(整数和小数转换)后都有比较合适的函数,而且它们在同一个优先级中,谁也不比谁优秀,调用哪个都一样,产生了二义性,所以编译器会报错。注意,类型提升和类型转换不是一码事!类型提升是积极的,是为了更加高效地利用计算机硬件,不会导致数据丢失或精度降低;而类型转换是不得已而为之,不能保证数据的正确性,也不能保证应有的精度。类型提升只有上表中列出的几种情况,其他情况都是类型转换。
多个参数时的二义性
当重载函数有多个参数时也会产生二义性,而且情况更加复杂。C++ 标准规定,如果有且只有一个函数满足下列条件,则匹配成功:- 该函数对每个实参的匹配都不劣于其他函数;
- 至少有一个实参的匹配优于其他函数。
假设现在有以下几个函数原型:
- void func(int, int); //①
- void func(char, int, float); //②
- void func(char, long, double); //③
- short n = 99;
- func('@', n, 99);
- func('@', n, 99.5);
func(int, int)
只有两个参数,而函数调用有三个参数,很容易看出来不匹配,在初次筛选时就会被过滤掉,接下来我们只讨论②③个函数原型。1) 先来看第一个函数调用。如果只考虑第一个实参
'@'
,那么②③两个函数都能够精确匹配,谁也不比谁优秀,是平等的;如果只考虑第二个实参n
,对于②,需要把 short 提升为 int(类型提升),对于③,需要把 short 转换为 long(类型转换),类型提升的优先级高于类型转换,所以②胜出;如果只考虑第三个实参99
,②③都要进行类型转换,没有哪一个能胜出,它们是平等的。从整体上看,②③在第一、三个实参的匹配中是平等的,但②在第二个实参的匹配中胜出,也就是说,②对每个实参的匹配都不劣于③,但有一个实参的匹配优于③,所以②最终脱颖而出,成为被调用函数。
2) 再来看第二个函数调用。只考虑第一个实参时②③是平等的,没有谁胜出;只考虑第二个实参时②胜出;只考虑第三个实参时,②需要类型转换,③能够精确匹配,精确匹配的优先级高于类型转换,所以③胜出。
从整体上看,②③在第一个实参的匹配中是平等的,②在第二个实参的匹配中胜出,③在第三个实参的匹配中胜出,它们最终“打成了平手”,分不清孰优孰劣,所以编译器不知道如何抉择,会产生二义性错误。
总结
在设计重载函数时,参数类型过少或者过多都容易引起二义性错误,因为这些类型相近,彼此之间会相互转换。例如我们要设计几个重载函数来处理数值,数值包括小数和整数,如果只提供了以下两个函数原型:
- void func(int);
- void func(double);
- long n = 1000;
- func(n);
如果添加一个函数原型
void func(long);
,或者去掉一个函数原型void func(int);
,无论再怎么调用也不会出错了。 阅读全文
4 0
- C++函数重载过程中的二义性和类型转换
- 类型转换和操作符重载 (c#)
- 【c/c++】类型转换函数(类型转换运算符重载函数)
- C语言中的函数指针类型转换
- java中的重载、重写(覆盖)和类型转换
- string重载和类型转换
- c++和Java中的方法(函数)重载与传入方法的基本类型转换——相同部分
- JNI中的类型和c语言中的类型转换
- 当心隐式类型转换导致重载函数产生二义性
- 隐式类型转换导致重载函数产生二义性
- c语言运算过程中的类型自动转换原则
- C类型转换函数
- C++ 重载函数:实参类型转换
- 重载类型转换中的运算符
- C#.NET 中的类型转换和进制转换
- 第十一章:运算符重载,友元函数,重载<<运算符,类的自动转换和强制类型转换
- 函数调用中的类型转换 c提升规则
- C++重载之转换构造函数与类型转换函数
- 3. Managing the lifecycle of a Basic Thread
- C小加 之 随机数
- HDU 3410 Passing the Message
- JEESZ分布式架构集成阿里云OSS存储
- js中方法的封装
- C++函数重载过程中的二义性和类型转换
- android7.0之前和7.0调用系统相机
- 数据库——(10)联合查询和子查询
- Jetty与Tomcat综合比较
- 希尔排序
- CSAPP-存储体系实验
- HTML5的localStorage实现记住密码的功能
- POST 发送工具类
- 问答1:哪个Sql更快