编译器为函数调用决定使用哪一种定义时,对函数重载,函数模板,函数模板重载有个决策过程,叫做重载解析(overloading resolution)。
大概过程如下:
1.创建候选函数列表 :编译器会从作用域中收集所有名称匹配的函数。这些函数称为候选函数。包含普通函数和模板函数;
2. 创建可行函数列表:首先从候选函数列表中筛选参数个数相同的函数,然后进一步通过隐式转换和模板实例化创建出可行函数列表;
3. 最后确认是否有可行函数,如果有则匹配最合适的那个,如果没有则调用出错;
举个例子,我想调用:
may('B');
编译器开始寻找和may同名的函数和函数模板,只考虑特征标不考虑返回类型:
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
然后从上述候选函数列表中筛选。
首先排除#4和#7,因为int类型无法隐式转换成指针类型。
然后按匹配优先级选择可行函数:
1. 完全匹配,但常规函数优先于模板;
2. 提升转换;
3. 标准转换;
4. 用户定义的转换;
按上面的标准,函数#1优先于函数#2,因为char到int是提升转换,char到float是标准转换。
函数#3,#5,#6又优先于函数#1,#2.因为它们是完全匹配。
函数#3,#5优先于函数#6,因为常规函数优先于模板。
有多个完全匹配的函数时,如何进行下一步的选择?举个例子:
struct blot
{
int a;
char b[10];
};
blot ink = {25, "sports"};
recycle(ink);
以下几个都是完全匹配的函数:
void recycle(blot); // #1 blot-to-blot
void recycle(const blot); // #2 blot-to-(const blot)
void recycle(blot &); // #3 blot-to-(blot &)
void recycle(const blot &); // #4 blot-to-(const blot &)
因为被调函数是非const参数,所以非const参数的指针和引用会优先匹配,在函数#3和#4中,会优先选择函数#3。但这个规则不适用于非指针和引用的参数,比如同时只有函数#1和#2的时候,就会出现函数调用二义性报错。
常规函数优先于模板函数,在多个模板函数之间,更具体的模板函数更优先。显示具体化优先与隐式转换的具体化。看下面这个例子:
template <class Type> void recycle (Type t);
template <> void recycle<blot> (blot & t);
recycle(ink); // 第二个函数模板更具体,优先使用
更具体不一定是说显式具体化,而是编译器认为需要执行的类型转换步骤更少。看下面两个模板:
template <class Type> void recycle (Type t); // #1
template <class Type> void recycle (Type *t); // #2
recycle(&ink);
recycle(&ink)与函数#1匹配时,Type被解释为blot*。
recycle(&ink)与函数#1匹配时,Type被解释为blot。
因此两个隐式实例:void recycle<blot*>(blot*)和void recycle<blot>(blot *)会被发送到可执行池。
void recycle<blot>(blot*)会被认为是更具体的。因为在生成过程中,它需要的转换更少。(这里书中原文写的晦涩难懂)
在<<c++ primer plus>>中提到:
作者应该是写错了,更具体的是模板recycle<blot>(blot *),因为从直觉上看,函数#1是用于非指针参数调用,函数#2用于是指针参数调用的。
模板 #2
(template <class Type> void recycle(Type * t);
) 更具体,因为它直接匹配指针类型,而模板 #1
(template <class Type> void recycle(Type t);
) 需要将传入的指针转换为 Type
。因为模板 #2
对于指针类型没有额外的类型转换,编译器会优先选择 #2
。
还可以手动引导编译器调用你想用的函数模板,举个例子:
#include <iostream>
template<class T>
T lesser(T a, T b) // #1
{
return a < b ? a : b;
}
int lesser(int a, int b) # 2
{
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 = -10;
double a = 1.0;
double b = -2.0;
cout << lesser(m, n) << endl; // #2
cout << lesser(a, b) << enld; // #1
cout << lesser<>(m, n) << endl; // #1
cout << lesser<int>(a, b) << endl; // #1
return 0;
}