概述
关于函数的重载机制,是一个比较复杂的问题,其中涉及到了优先级定义和最佳匹配等问题,如果要阐述清楚,恐怕不是一两篇文章就能说的明白。但是如果掌握了一些常用的“规律”,对于了解程序对重载函数是如何进行选择也有很大的好处,本文尝试将自己理解的知识,结合下面简单的例子简略的说说函数重载机制,文章的摘录部分列出了一些关于程序如何选择重载函数的规则。: )
例子如下:
#include <iostream>
//non-template function
inline const int& max( const int& a, const int& b )
{
std::cout << "non-template max() is called" << std::endl;
return a < b ? b : a;
}
//template function
template < typename T >
inline const T& max( const T& a, const T& b )
{
std::cout << "template max() is called" << std::endl;
return a < b ? b : a;
}
int main()
{
::max( 7.0, 42.0 ); //template max() is called
::max( 'a', 'b' ); //template max() is called
::max( 7, 42 ); //non-template max() is called
::max<>( 7, 42 ); //template max() is called
::max< double >( 7, 42 ); //template max() is called
::max( 'a', 42.7 ); //non-template max() is called
}
程序顺利通过编译,并运行得出结果,说明同名的非函数模板函数和函数模板可以共存。程序会通过优先级和最佳匹配的方式从候选的重载函数集中选定一个函数进行调用(所遵循的规则见后面的摘录部分)。
程序的输出结果见每个函数调用的注释,对于第一和第二个输出结果想必应该没有太多的疑问,程序调用的是函数模板max,下面主要分析其余的输出。
【一】、max( 7, 42 );调用的是非函数模板max()。当其它的要素都相等时,重载机制将优先选择调用非函数模板而不是函数模板【对于这个问题,个人觉得可能是基于如下的原因:进行重载将降低程序的效率,对非函数模板是如此,对于更为复杂的函数模板更是如此(至少还需进行一次实例化),因此重载机制将优先选择调用非函数模板而不是函数模板。】。那些无法跟非函数模板进行最佳匹配的,则调用函数模板的实例化对象,如第一和第二个函数调用。
【二】、max<>( 7, 42 );跟max( 7, 42 );的唯一区别是前者多了一个模板参数列表,还记得前面笔记中说到的函数模板参数的问题么?<>中的参数用于指定函数模板中,传入的参数类型跟返回值类型,列表中参数的顺序对应于模板中声明的类型的顺序。这里的参数列表为空,但却告诉了编译器,这个函数只在函数模板中选择最佳匹配的函数调用。同样的,max< double >( 7, 42 ); 调用的是函数模板的一个实例化对象,这里指定了模板参数的类型,因此对于传入的值,程序会对其进行一个转换(从int转为double),然后比较大小。
【三】、对于最后一个函数调用max( 'a', 42.7 );一开始我认为是调用非函数模板,结果确实也是调用了非函数模板,我的理由是两个参数的类型明显不同,后面看到书上的解释,是这么说的:自动类型转换,只适用于一般函数(即非函数模板)。其实我的理解也没有什么偏差,只是不够深刻。如果觉得'a'(char型)跟42.7(程序默认为double型)相差太大,不好理解上面那句话,试一下这个调用max( 'a', 42 );(一般我们都会对char和int划上一个隐式的等号,默认char就是int的子集,但事实上还是要经过一个隐式的转换)结果也是调用了非函数模板max()。
//====================【附录:关于重载的一些说明】====================
这部分的内容主要摘自《c++template》中的附录,有少许语言组织上的改动。另外,《c++primer》中对重载也有比较详细的讨论。
【一】何时会进行重载?
首先,如果是通过函数指针或者成员函数指针来进行调用,就不会进行重载解析,因为究竟调用的是哪个函数是在运行期由指针(实际上所指向对象)来决定的。其次,类似函数的宏不能被重载,因此也不会进行重载解析。
【二】重载是个什么样的过程?
-
查找名称,从而形成一个初始化的重载集(合)。
-
如果有必要,会用各种方法对这个集合进行修改(例如,发生模板演绎的时候)。
-
任何与调用不匹配(即使考虑了隐式转换和缺省实参之后仍然不匹配)的候选函数都从重载集中删除,最后得到的集合就是:可行的候选函数集。
-
执行重载解析来寻找一个最佳候选函数。如果能找到,则选择这个最佳候选函数;否则,这个调用就是二义性的。
-
检查这个被选定的最佳候选函数。例如,如果它具有不能访问的私有成员,则可能会给出诊断信息。
【三】关于上面的说到的重载解析,是根据什么原则来选定最佳候选函数?
-
完美匹配。参数的类型和实参(表达式)的类型相同,或者参数的类型是指向实参类型的引用(也可以增加const或者volatile限定符)。
-
有细微调整的匹配。如数组转变为指向数组第一个元素的指针,或者添加const,从而让类型为int**的实参匹配类型为int const* const*的参数等。
-
发生提升的匹配。提升是一种隐式类型转换,它包含把占位少的整数类型(如bool,char,short或者某些枚举)转换为占位多的类型(如int,unsigned int,long或者unsigned long),还包括从float到double的类型转换。
-
发生标准转换(类型转换)的匹配。这包含任何种类的标准转型(如int到float),但并不包含隐式调用的类型转换运算符和单参数构造函数。
-
发生用户自定义转换的匹配。这允许任何种类的隐式类型转换。
-
和省略号的匹配。省略号参数可以匹配任何类型(但匹配非POD(plain old data)类型会导致未经定义的行为)
最后
以上就是热情秋天为你收集整理的C++ 模板函数和非模板函数的重载的全部内容,希望文章能够帮你解决C++ 模板函数和非模板函数的重载所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复