Function Templates 函數模板——初窺

1.1Function Template初窺

所謂的function templates是指由參數化手段表現一整個族羣的function。

一個function templates可以表示一族(一整羣)functions,其表現和一般的function並無二致,只是其中某些元素在編寫時尚未確定,換而言之,那些[尚未確定的元素]被[參數化]了。

1.1.1定義Template
下面的function templates傳回兩個數值中的較大者:

template<typename T>
inline T 
const & max( T const & a, T const & b )
{
    
return a < b ? b : a
}

這一份template定義式代表了一整族functions,他們的作用都是傳回a和b兩參數中的較大者。兩個參數的型別都尚未確定,我們說它"template parameter T"。如你所見,template parameters必須以如此形式加以宣告:

 template<以逗號分隔的參數列>
上述例子中,參數列就是typename T。請注意,例子中的[<]和[>]在這裏被當作尖括號使用。關鍵字typename引入了一個所謂的type parameter(型別參數)------這是目前爲止C++程式中最常使用的一種template parameter,另還存在其他種類的template parameter(如:nontype parameter,[非型別參數]),這個以後討論。

此處的型別參數是T,你也可以使用其他任何標識符號(identifier)來表示型別參數,但習慣寫成T(譯註:代表Type)。Type parameter可標識任何型別,在function templates被呼叫時,經由傳輸具體型別而使T得以被具體指定。你可以使用任何型別,只要它支持T所要完成的操作。本列中型別T必須支援operator<以比較兩值大小。

由於歷史因素,你也可以使用關鍵字class代替關鍵字typename來定義一個type parameter。關鍵字typename是C++發展晚期才引進的,在此之前只能經由關鍵字class引入type parameter。關鍵字class目前依然可以用。因此template max()也可以被寫成如下等價形式:

template<class T>
inline T 
const & max(T const & a, T const & b)
{
    
return a < b ? b : a;
}

就語義而言,前後兩者毫無區別。即便使用關鍵字class,你還是可以把任意型別(包括non-class型別)當作實際的template arguments。但是這麼寫可能帶來一些誤導(讓人誤以爲T必須是class型別),所以最好還是使用關鍵字typename。請注意,這和class的型別宣告並不是同一回事:宣告type parameters時我們不能把關鍵字typename換成關鍵字struct。

1.1.2使用Template
以下程式示範如何使用max() function template:

#include <iostream>
#include 
<string>
#include 
"max.hpp"

int main()
{
    
int i = 42;
    std::cout
<<"max(7,i): "<<::max(7,i)<<std::endl;
 
    
double f1 = 3.4;
    
double f2 = -6.7;
    std::cout
<<"max(f1,f2): "<<::max(f1,f2)<<std::endl;

    std::
string s1 = "mathematics";
    std::
string s2 = "math";
    std::cout
<<"max(s1,s2): "<<::max(s1,s2)<<std::endl;
}

程序呼叫了max()三次。第一次參數是兩個int,第二次參數是兩個double,最後一次參數是兩個std::string。每一次max()均比較兩值取大者。程序運行結果爲:

max(7,i): 42
max(f1,f2): 
3.4
max(s1,s2): mathematics

注意程序對max()的三次呼叫都加了前綴符號"::",以遍確保被喚起的是我們在全域名空間(global namespace)中定義的max()。標準程序庫內也有一個std::max() template,可能會在某些情況下被喚起,或在呼叫時引起模棱兩可(ambiguity,歧義性,如果某個引數的型別定義於namespace std中,例如string,根據C++搜尋規則,::max()和std::max()都會被找到,那就會引起歧義性)。

 一般而言,templates不會被編譯爲[能夠處理任意型別]的單一實物(entity),而是被編譯爲多個個別實物,每一個處理某一特定型別。([一份實物,適用所有型別],理論上成立,實際不可行。畢竟所有語言規則都奠基於[將會產生出不同實物]的概念all language rules are based on the concept that different entities are generated)。因此,針對三個型別,max()被編譯成三個實物。

例如第一次呼叫max():
int i = 42;
...max(
7,i)...

使用的是[以int爲template parameter T]的funciton template,語義上等同於呼叫以下函數:

inline int const & max(int const & a, int const & b)
{
    
return a < b ? b : a;
}

以具體型別替換template parameters的過程稱爲[具現化](instantiation,或稱[實體化])。過程中會產生template的一份實體(instance)。不巧的是,instantiation和instance這兩個術語在OO(面向對象)編程領域中有其他含義,通常用來表示一個class的具體物件(concrete object)。

注意,只要function template被使用,就會自動引發具現化過程。程序員沒有必要個別申請具現過程。

類似情況,另兩次對max()的呼叫被具現化爲:

const double & max(double const &double const &);
const std::string & max(std::string const &, std::string const &);

 如果試圖以某個型別來具現化function template,而該型別並未支援function template中用到的操作,就會導致編譯錯誤。例如:

std::complex<float>c1, c2;  //此型別並不提供operator<
... 
max(c1,c2);                 
//編譯期出錯

實際上,template會被編譯兩次:

1.不具現化,只是對template程序代碼進行語法檢查以發現諸如[缺少分號;]等等的語法錯誤。

2.具現化,編譯器檢查template程序代碼中的所有呼叫是否合法,諸如[未授支援函數調用]便會在這個階段被檢查出來。

這會導致一個嚴重問題:當function template被運用而引發具現化過程時,某些時候編譯器需要用到template的原始定義。一般情況下,對普通的(non-template)functions而言,編譯和連結兩步驟是各自獨立的,編譯器只檢查各個functions的宣告式是否和呼叫式相符,然後template的編譯破壞了這個規則。眼下我們用最簡單的解法:把template程序代碼以inline形式寫在表頭檔(header)中。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章