刚刚看了一篇有关函数重载的文章,在文章的结尾作者抛出了一个问题,模板函数的普通函数构成的重载调用时是如何分配的????对于白痴一枚的我嫩是半天都木有思绪,关于函数的重载机制,是一个比较复杂的问题,结合下面简单的例子简略的说说函数重载机制

例:

#include <iostream>

using namespace std;

 const int& max( const int& a, const int& b )

{

cout << "non-template max() is called" << endl;

return a < b ? b : a;

}

//template function

template < typename T >

inline const T& max( const T& a, const T& b )

{

cout << "template max() is called" << 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

system("pause");

return 0;

}

wKioL1b2hDjgSJq5AAAub1xaUyE055.png

程序顺利通过编译,并运行得出结果,说明同名的非函数模板函数和函数模板可以共存。程序会通过优先级和最佳匹配的方式从候选的重载函数集中选定一个函数进行调用。

对于第一和第二个输出结果想必应该没有太多的疑问,程序调用的是函数模板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 );(一般我们都会对charint划上一个隐式的等号,默认char就是int的子集,但事实上还是要经过一个隐式的转换)结果也是调用了非函数模板max()

 有关重载:

【一】何时会进行重载?

首先,如果是通过函数指针或者成员函数指针来进行调用,就不会进行重载解析,因为究竟调用的是哪个函数是在运行期由指针(实际上所指向对象)来决定的。其次,类似函数的宏不能被重载,因此也不会进行重载解析。

【二】重载是个什么样的过程?

  • 查找名称,从而形成一个初始化的重载集(候选函数集)。

  • 如果有必要,会用各种方法对这个集合进行修改。

  • 任何与调用不匹配(即使考虑了隐式转换和缺省实参之后仍然不匹配)的候选函数都从重载集中删除,最后得到的集合就是:可行的候选函数集。

  • 执行重载解析来寻找一个最佳候选函数。如果能找到,则选择这个最佳候选函数;否则,这个调用就是二义性的。

  • 检查这个被选定的最佳候选函数。例如,如果它具有不能访问的私有成员,则可能会给出诊断信息。

【三】关于上面的说到的重载解析,是根据什么原则来选定最佳候选函数?

  • 完美匹配。参数的类型和实参(表达式)的类型相同,或者参数的类型是指向实参类型的引用(也可以增加const或者volatile限定符)。

  • 有细微调整的匹配。如数组转变为指向数组第一个元素的指针,或者添加const,从而让类型为int**的实参匹配类型为int const* const*的参数等。

  • 发生提升的匹配。提升是一种隐式类型转换,它包含把占位少的整数类型(如boolcharshort或者某些枚举)转换为占位多的类型(如intunsigned intlong或者unsigned long),还包括从floatdouble的类型转换。

  • 发生标准转换(类型转换)的匹配。这包含任何种类的标准转型(如intfloat),但并不包含隐式调用的类型转换运算符和单参数构造函数

  • 发生用户自定义转换的匹配。这允许任何种类的隐式类型转换。

  • 和省略号的匹配。省略号参数可以匹配任何类型(但匹配非PODplain old data)类型会导致未经定义的行为)