函數模板
首先我們來看看函數模板,一個函數模板(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;
}
運行結果為:
大家可以注意編譯器優先選擇那些特化的模板。
另外建議大家在重載函數模板的時候,不同的重載形式之間最好存在絕對必要的差別,并且請把所有形式的重載函數寫在他們的被調用點之前。