- 函数模板可以被另一个模板或者一个非模板函数重载,名字相同的函数必须具有不同数量或类型的参数。
- 设计到函数模板,函数匹配规则满足:
- 对于一个调用,其候选函数包括所有模板实参推断成功的函数模板实例
- 候选函数总是可行的,因为模板实参推断会排出任何不可行的模板
- 可行函数按照类型转换来排序
- 如果恰有一个函数提供比其他函数都更好的匹配,则选择此函数,如果有多个函数提供同样好的匹配,则:
- 如果同样好的函数中只有一个非模板函数,则选择此函数
- 如果没有非模板函数,有多个函数模板,则选择其中一个比其他模板更特例化的模板
- 否则此调用有歧义
编写重载模板
- 下面函数可以生成一个对象对应的string表示,该对象是任何具备输出运算符的类型
//打印任何我们不能处理的类型
template<tyename T>string debug_rep(const T &t)
{
ostringstream ret;
ret << t;
return ret.str();//返回ret绑定的string副本
}
- 下面是打印指针的debug_rep版本
//打印任何我们不能处理的类型
template<tyename T>string debug_rep(const T *t)
{
ostringstream ret;
ret << t;
if(t)
ret << "pointer:" << p;
else
ret << "null pointer";
return ret.str();//返回ret绑定的string副本
}
string s("hello");
debug_rep(s);//调用第一个版本的debug_rep
- 只有第一个版本的debug_rep是可行的,第二版本要求一个指针参数
debug_rep(&s);//调用第二版本的debug_rep
- 第一版本能生成实例,其T的类型是string *,需要进行普通指针到const指针的转换
- 第二版本T类型为string,此为更精确匹配
多个可行版本
- 考虑一下代码:
const string *sp = &s;
cout << debug_rep(sp) <<endl;
- 两个版本都是可行的,都是精确匹配
- 第一个版本实例为:
debug_rep<string&>(const string *&)
,T被绑定为string* - 第二个版本实例为:
debug_rep<const string>(const string *)
,T被绑定为const string - 由于第二版本比第一版本更加特例化,固解析为第二版本。
非模板和模板重载
string debug_rep(const string &s)
{
return'"'+s+'"';
}
- 考虑下边代码
string s("hello");
debug_rep(s);//调用第三个非模块版本的debug_rep
- 第一和第三个版本都能提供同样好的匹配
- 第一版本实例化为
debug_rep<string>(const string &)
,T被绑定到string
- 第一版本实例化为
- 存在同样好的模板和非模板函数时,编译器会选择非模板函数,当存在多个同样好的非模板函数时,编译器会选择最特例化的版本,一个非模板函数比模板函数更加特例化
重载与类型转换
考虑下边代码
cout << debug_rep("hello");//调用第二版本
上述三个版本都可行
- debug_rep(const T&),T为char[6]
- debug_rep(T*),T为const char*
- debug_rep(const string&),要求从const char*到string的类型转换
对给定的实参来说,第二个版本需要一次数组到指针的转换,这是精确匹配
- 非模板版本需要一次用户定义的类ixngzhuanhuan,因此没有精确匹配那么好
缺少生命可能导致程序行为异常
- 使用一个忘记声明的函数,代码将会编译失败
- 对于重载函数模板的函数,如果编译器可以从模板实例化出与调用匹配的版本,则缺少的声明就不重要了