函数模板
首先我们来看看函数模板,一个函数模板(function template)代表一族函数,其表现和一般的函数一样,只是其中的某些元素在编写的时候还不知道,也就是说这些还不知道的元素,我们将其参数化了。
例如下面的返回两个数中的较大者:
template<class T> inline T const& max(T const& a,T const& b){ return a>b?a:b; }
当然,上述代码中的class也可以用typename所代替,但是不能使用struct代替。不过一般建议使用typename。
举个可以实际运行的例子:
#include <iostream> #include <string> template<class T> inline T const& max(T const& a,T const& b){ return a>b?a:b; } int main(){ std::cout<< ::max(2,1)<<std::endl; std::cout<< ::max(1.12,4.5)<<std::endl; std::string str1="hello"; std::string str2="rollen"; std::cout<< ::max(str1,str2)<<std::endl; return 0; }
注意上面使用了::max,是为了和std::max进行区分的。
但是如果试图使用某一类型的时候,但是这个类型中并没有定义我们在模板函数中所使用的某一些操作的时候,就会出现错误。
std::complex<float> c1,c2; max(c1,c2);
上面的代码在编译期间就会出现错误。
实际上,我们的template会被编译两次:
第一次:主要是对代码进行语法检查,比如缺少分号,什么的。
第二次: 主要是对template代码中所进行的操作进行检查,就如同上面的那样,是否使用了未定义的操作等等。
其实这样会导致一些问题的,我们在后面的内容中会探讨这个问题。
template<class T> inline T const& max(T const& a,T const& b){ return a>b?a:b; } std::cout<< ::max(2,1)<<std::endl; //编译器可以推导出是两个int
std::cout<< ::max(1.12,4.5)<<std::endl; //编译器可以推到出是两个double
//std::cout<< ::max(1 , 1.2)<<std::endl; //这条错误
函数模板的参数分为两种:template parameter和call parameter参数两种。比如对于上面的max代码,其中的T是template parameter参数,a,b是call parameter参数。前者的数量可以是任意的,但是你不能在函数模板中为他们设定初始值,这一点和class template是不一样的,后面会提到。
对于上面代码中的那一条错误语句,其实你可以改为下面的语句:
std::cout<< ::max<double>(1 , 1.2)<<std::endl;
但是如果template parameter和call parameter参数没有明显的联系的时候,并且编译器无法推断出template parameter的时候,你就需要明确的指定template argument,例如你可以在max中引入第三个template argument type 作为返回类型:
template<typename T1,typename T2,typename RT> inline RT max(T1 const& a, T2 const& b); max<int,double,double>(1,2.3);
但是这样的话,需要在max的尖括号中写3个参数,其实我们至于要改变一些RT的顺序,就可以只需要写一个参数就行了:
template<typename RT,typename T1,typename T2> inline RT max(T1 const& a, T2 const& b); max<double>(1,2.3);
在这个例子中,只要我们明确的指出返回类型,然后编译器就可以自动推断出a和b的类型;
关于模板函数的重载问题:
首先来看看一个小例子:
#include <iostream> #include <string> #include <cstring> template<class T> inline T const& max(T const& a, T const& b){ std::cout<<"inline T const& max(T a, T b)"<<std::endl; return a>b ? a :b; } template<class T> inline T* const& max(T* a, T* b){ std::cout<<"inline T* const& max(T* a, T* b)"<<std::endl; return *a > *b ?a: b; } inline char const* const& max(char const* const& a, char const* const& b){ std::cout<<"inline char const* const& max(char const* const& a, char const* const& b)"<<std::endl; return std::strcmp(a,b)<0?b:a; } int main(){ int a=1,b=2; ::max(a,b); std::string str1="hello"; std::string str2="rollen"; ::max(str1,str2); int* pa=&a; int* pb=&b; ::max(pa,pb); char const* s1="hello"; char const* s2="rollen"; ::max(s1,s2); return 0; }
运行结果为:
大家可以注意编译器优先选择那些特化的模板。
另外建议大家在重载函数模板的时候,不同的重载形式之间最好存在绝对必要的差别,并且请把所有形式的重载函数写在他们的被调用点之前。