定義模板
函數模板
template <typename T>
int compare (const T &v1, const T &v2){
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
當我們調用一個函數模板時,編譯器用函數實參來爲我們推斷模板實參。
cout<<compare(1,0)<<endl;
除了定義類型參數,還可以在模板中定義非類型參數。一個非類型參數表示一個值而非一個類型。我們通過一個特定的類型名而非關鍵字class或typename來指定非類型參數
</font color=“red”>當一個模板被實例化時,非類型參數被一個用戶提供的或編譯器推斷出的值所代替。這些值必須是常量表達式,從而允許編譯器在編譯時實例化模板。
例如:
template<unsigned N,unsigned M>
int compare(const char (&p1)[N],const char (&p2)[M]){
return strcmp(p1,p2);
}
編譯器會使用字面常量的大小來代替N和M,從而實例化模板。
在模板定義內,模板非類型參數是一個常量值。在需要常量表達式的地方,可以使用非類型參數,例如,指定數組大小。
編寫泛型代碼的兩個重要原則:
- 模板中的函數參數是const的引用
- 函數體中的條件判斷僅使用<比較運算
通過將函數參數設定爲const的引用,我們保證了函數可以用於不能拷貝的類型。大多數類型,包括內置類型和我們已經用過的標準庫類型(除unique_ ptr 和I0類型之外),都是允許拷貝的。但是,不允許拷貝的類類型也是存在的。通過將參數設定爲const 的引用,保證了這些類型可以用我們的compare函數來處理。而且,如果compare用於處理大對象,這種設計策略還能使函數運行得更快。
當編譯器遇到一個模板定義時,它並不生成代碼。只有當我們實例化出模板的一個特定版本時,編譯器纔會生成代碼。當我們使用(而不是定義)模板時,編譯器才生成代碼, .這一特性影響了我們如何組織代碼以及錯誤何時被檢測到。
通常,當我們調用一個函數時,編譯器只需要掌握函數的聲明。類似的,當我們使用一個類類型的對象時,類定義必須是可用的,但成員函數的定義不必已經出現。因此,我們將類定義和函數聲明放在頭文件中,而普通函數和類的成員函數的定義放在源文件中。
模板則不同:爲了生成一個實例化版本,編譯器需要掌握函數模板或類模板成員函數的定義。因此,與非模板代碼不同,模板的頭文件通常既包括聲明也包括定義。
類模板
類模板與函數模板的不同之處是:**編譯器不能爲類模板推斷模板參數類型。**爲了啥用類模板,我們必須在模板名後的尖括號提供額外信息。
類模板與友元
//前置聲明,在Blob中聲明友元所需要的
template <typename>
class BlobPtr;
template <typename>
class Blob; // 運算符==中的參數所需要的
template <typename T>
bool operator==(const Blob<T>&, const B1ob<T>&) ;
template <typename T>
class Blob {
//每個Blob實例將訪問權限授予用相同類型實例化的BlobPtr和相等運算符
friend class B1obPtr<T>;
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
};
類模板與static
類模板的每個實例都有一個獨有的static對象。因此,與定義模板的成員函數類似,我們將static數據成員也定義爲模板:
template<typename T>
size_t Foo<T>::ctr = 0; //定義並初始化ctr
模板實例化
函數實例化
//compare的特殊版本,處理字符數組的指針
template<>
int compare(const char* const& p1,const char* const& p2){
return strcmp(p1,p2);
}