1 函数模板
1.1 初探函数模板
函数模板的定义
templates<comma-separated-list-of-parameters>
….
l 模板函数会进行实参演绎,所以在使用函数模板的时候,一般不需要制定参数的类型。
l 就编译原理来说,并不是把模板编译成一个可以处理任何类型的单一实体,而是对于实例化模板参数的每种类型,都从模板产生出一个不同的实体。
l 如果试图基于一个不支持模板内部所使用的操作的类型实例化一个模板,那么将会导致一个编译期错误,由此,我们可以得出结论:模板被编译了两次:
n 实例化之前,先检查模板代码本身,看语法是否正确;
n 在实例化期间,检查模板代码,查看是否所有的模板都有效。
l 由于上面提到的,模板要被实例化,所以在编译的时候,编译期就需要知道模板的定义。这就不同于普通函数中编译和链接之间的区别,因为对于普通函数而言,只要有函数的声明就可以编译通过,但是模板函数不行,为此,一般来说,模板函数都采用内联函数的方式。
1.2 模板参数
这里要明白两个概念:
l 模板参数,位于尖括号内;
l 调用参数,位于圆括号内;
template<typename T1, typename T2>
inline T1 max(T1
const& a, T2 const&)
{
return
a<b? b:a;
}
注意上面的函数的返回值为T1,而不是T1 const&,此处必须是T1,因为在将T2 const&转型为T1的过程中会产生一个局部变量,而局部变量是不能作为引用返回的。
如果有多个模板参数,你可以只显式指定第一个实参,而让演绎过程推导出其余的参数。通常来说,你必须指定“最后一个不能被演绎的模板实参之前的”所有实参类型。
例如:
Template<typename RT, typename T1, typename T2>
Inline RT max(T1 const& a, T2 const& b)
{
return a<b?
b:a;
}
1.3 重载模板函数
首先,重载就是一个很难的话题,这里头有一些细节是一般人(包括我)所不了解的。要了解重载的细节,可以参考本书附录。
我们来看个例子:
//(1)
Inline int const& max(int const& a, int const& b)
{ return a < b? b:a;}
//(2)
Template<typename T>
Inline T const& max(T const& a , T const& b)
{ return a < b? b:a;}
//(3)
Template<typename T>
Inline T const& max(T const& a , T const& b, T const& c)
{ return ::max(max(a,b),c); }
Int main()
{
::max<>(7, 42); //调用2
::max(‘a’, 42.7); //调用1
}
l 在其他函数都相同的情况下,在调用的时候,重载解析过程通常会调用非模板函数,而不会从该模板产生出一个实例。这站在编译器的角度来看,也容易理解。因为如果再从模板实例化一个,则会使代码变得更加庞大,而且产生重复。
l 模板是不允许进行自动类型转换的,所以上面的第二个调用将调用(1),而不是(2)。
l 一般来说,在重载函数模板的时候,最好只改变那些需要改变的内容,也就是说,你应该吧你的改变限制在下面两种情况:改变参数的数目,或者显式的指定模板参数,否则可能导致一些非预期的行为,具体例子见本书p18.
l 函数的所有重载版本的声明应该位于该函数被调用的位置之前。