引言:
所謂泛型程式就是以獨立于任何特定類型的方式編寫代碼。使用泛型程式時,我們需要提供具體程式執行個體所操作的類型或值。
模闆是泛型程式設計的基礎。使用模闆時可以無須了解模闆的定義。
泛型程式設計與面向對象程式設計一樣,都依賴于某種形式的多态性。面向對象程式設計中的多态性在運作時應用于存在繼承關系的類。我們能夠編寫使用這些類的代碼,忽略基類與派生類之間類型上的差異。隻要使用基類的引用或指針,基類類型或派生類類型的對象就可以使用相同的代碼。
在泛型程式設計中,我們所編寫的類和函數能夠多态地用于跨越編譯時不相關的類型。一個類或一個函數可以用來操縱多種類型的對象。标準庫中的容器、疊代器和算法是很好的泛型程式設計的例子。标準庫用獨立于類型的方式定義每個容器、疊代器和算法,是以幾乎可以在任意類型上使用标準庫的類和函數。
在C++中,模闆是泛型程式設計的基礎。模闆是建立類或函數的藍圖或公式。
編寫重載函數:
這些函數幾乎相同,它們之間唯一的差別是形參的類型,每個函數的函數體是相同的。
每個要比較的類型都需要重複函數的函數體,不僅麻煩而且容易出錯。更重要的是,需要事先知道空間可能會比較哪些類型。如果希望将函數用于未知類型,這種政策就不起作用了。
一、定義函數模闆
我們可以不用為每個類型定義一個新函數,而是定義一個函數模闆。函數模闆是一個獨立于類型的函數,可以作為一種方式,産生函數的特定類型版本。
模闆定義以關鍵字template開始,後接模闆形參表,模闆形參表是用尖括号包覆的一個或多個模闆形參的清單,形參之間以逗号分隔。而且模闆形參表不能為空。
1、模闆形參表
模闆形參表類似于函數形參表,表示可以在類或函數的定義中使用的類型或值。例如,compare 函數聲明一個名為T的類型形參。在compare内部,可以使用名字T引用一個類型,T表示哪個實際類型由編譯器根據所用的函數而确定。
模闆形參可以是表示類型的類型形參,或者是表示常量表達式的非類型形參。類型形參跟在關鍵字class或typename之後定義。
2、使用函數模闆
使用函數模闆時,編譯器會推斷哪個(或哪些)模闆實參綁定到模闆形參。一旦編譯器确定了實際的模闆實參,就稱它執行個體化了函數模闆的一個執行個體。
推導出實際模闆實參後,編譯器使用實參代替相應的模闆形參産生編譯該版本的函數。
3、inline函數模闆
inline說明符放在模闆形參表之後、傳回類型之前,不能放在關鍵字template之前。
二、定義類模闆
為了舉例說明類模闆,我們将為标準庫queue類實作一個自己的版本。
我們自定義的Queue類必須能夠支援不同類型的對象,是以将它定義為類模闆。Queue所能支援的操作:
1)push:在隊尾添加一項
2)pop:從隊頭删除一項
3)front:傳回隊頭的引用
4)empty:指出隊列是否為空
類模闆也是模闆,是以必須以關鍵字template開頭,後接模闆形參表。
除了模闆形參表外,類模闆的定義看起來與任意其他類相似。在類和類成員的定義中,可以使用模闆形參作為類型或值的占位符,在使用類時再提供那些類型或值。
使用類模闆
與調用函數模闆形成對比,使用類模闆時,必須為模闆形參顯式指定實參:
編譯器使用實參來執行個體化這個類的特定類型版本。實質上,編譯器用使用者提供的實際特定類型代替Type,重新編寫Queue類。在這個例子中,編譯器将執行個體化三個Queue類:第一個用int代替 Type,第二個用vector<double>代替 Type,第三個用string代替 Type。
三、模闆形參
像函數形參一樣,為模闆形參選擇的名字沒有本質含義:
該代碼與前面定義的compare模闆一樣。
可以給模闆形參授予的唯一含義是差別是類型形參還是非類型形參。如果是類型形參,我們就知道該形參表示未知類型,如果是非類型形參,我們就知道它是一個未知值。
如果希望使用模闆形參所表示的類型或值,可以使用與對應模闆形參相同的名字。例如,compare函數中所有的Glorp引用将在該函數被執行個體化時确定為同一類型。
1、模闆形參作用域
模闆形參的名字可以在聲明為模闆形參之後直到模闆聲明或定義的末尾處使用。
模闆形參遵循正常名字屏蔽規則:
2、使用模闆形參名字的限制
用作模闆形參的名字不能在模闆内部重用:
這一限制還意味着模闆形參的名字隻能在同一模闆形參表中使用一次:
正如可以重用函數形參名字一樣,模闆形參的名字也能在不同模闆中重用:
3、模闆聲明
像其他任意函數或類一樣,對于模闆可以隻聲明而不定義。聲明必須指出函數或類是一個模闆:
同一模闆的聲明和定義中,模闆形參的名字不必相同:
每個模闆類型形參前面必須帶上關鍵字class或typename,每個非類型形參前面必須帶上類型名字,省略關鍵字或類型說明符是錯誤的: