1、所謂泛型程式設計就是以獨立於任何特定類型的方式編寫代碼。使用泛型程式時,我們需要提供具體程式執行個體所操作的類型或值。
在泛型程式設計中,我們所編寫的類和函數能夠多態地用於跨越編譯時間不相關的類型。
2、模板是泛型程式設計的基礎。
3、物件導向編程的多態性稱為運行是多態性,應用於存在繼承關係的類,我們能夠編寫這樣的代碼,忽略於基類與衍生類別之間的類型差異。
泛型程式設計所依賴的多態稱為編譯時間多態性或參數式多態性。
4、模板定義以關鍵字 template 開始,後接模板形參表,模板形參表是用角括弧括住的一個或多個模板形參的列表,形參之間以逗號分隔。
模板形參表不可為空。
範例程式碼
template <typename T>returntype functionname(parameter_list){//..}
5、模板形參可以是表示類型的類型形參,也可以是表示常量運算式的非類型形參。非類型形參跟在類型說明符之後說明。
6、使用函數模板時,編譯器會推斷那個(或那些)模板實參綁定到模板形參。一旦編譯器確定了實際的模板實參,就稱它為執行個體化了的函數模板的一個執行個體。
7、inline函數模板
如同非模板函數一樣聲明;注意inline的位置。
樣本
//ok: inline specifier follows template parameter listtemplate <typename T> inline T min(const T&, const T&);//error: incorrect placement of inline specifierinline template <typename T> T min(const T&, const T&);
8、類模板
同函數模板一樣,在類定義前面加template <class Type>。
9、可以給模板形參賦予的唯一含義是區別形參是類型形參還是非類型形參。如果是類型形參,我們就知道該形參表示未知類型,如果是非類型形參,我們就知道它是一個未知值。
10、範圍:模板形參的名字可以在聲明為模板形參之後直到模板聲明或定義的末尾處使用。模板形參遵循常規名字屏蔽規則。用作模板形參的名字不能在模板內部重用,可以在不同模板中重用。
11、可以只聲明,不定義模板。同一模板的聲明和定義中,模板形參的名字不必相同。每個模板類型形參前面必須帶上關鍵字typename/class,每個非類型形參前面必須帶上類型名字。
範例程式碼
// all three uses of calc refer to the same function template// forward declarations of the templatetemplate <class T> T calc(const T&, const T&) ;template <class U> U calc(const U&, const U&) ;// actual definition of the templatetemplate <class Type>Type calc(const Type& a, const Type& b) { /* ... */ }
12、模板類型形參可作為類型說明符用在模板中的任何地方:傳回型別,函數形參類型,變數聲明,強制類型轉換。
13、typename與class相同含義,而typename更直觀。
14、由類型形參定義的名字可能是一個類型,也可能是一個成員值。要通過typename顯示通知編譯器這是一個類型。
樣本
template <class Parm, class U>Parm fcn(Parm* array, U value){Parm::size_type * p; // If Parm::size_type is a type, then a declaration// If Parm::size_type is an object, then multiplication//typename Parm::size_type *p //ok, declares p to be a pointer}
15、非類型形參:調用函數時非類型形參將用值代替,值的類型在模板形參表中指定。
樣本
// initialize elements of an array to zerotemplate <class T, size_t N> void array_init(T (&parm)[N]){for (size_t i = 0; i != N; ++i) {parm[i] = 0;cout << i << endl;}}int main(){int x[42];array_init<int, 42>(x); //instantiates array_init(int(&)[42]) //array_init(x);//...return 1;}
對模板的非類型形參而言,求值結果相同的運算式將認為是等價的。
16、編寫模板代碼時,對實參類型的要求儘可能少是很有益的。
17、產生模板的特定類型執行個體的過程稱為執行個體化。模板在使用時將進行執行個體化,類模板在引用實際模板類類型時執行個體化,函數模板在調用它或用它對函數指標進行初始化或賦值時執行個體化。
18、類模板的每次執行個體化都會產生一個獨立的類類型。類模板的特定的執行個體化是通過提供模板實參與每個模板形參匹配定義的。使用函數模板時,編譯器通常會為我們推斷模板實參。
19、從函數實參確定模板實參的類型和值的過程叫做模板實參推斷。多個類型的形參與實參必須完全符合。
20、一般而言,不會轉換實參以匹配已有的執行個體化,會產生新的執行個體。除了產生新的執行個體化之外,編譯器只會執行兩種轉換。
1)const轉換:接受const引用或const指標的函數可以分別用非const對象的引用或指標來調用,無須產生新的執行個體。如果函數接受非參考型別,形參類型和實參都忽略const,即無論傳遞const或非const對象給接受非參考型別的函數,都使用相同的執行個體化。
2)數組或函數到指標的轉換:如果模板形參不是參考型別,則對數組或函數類型的實參應用常規指標轉換。數組實參將當作指向其第一個元素的指標,函數實參當作指向函數類型的指標。
樣本
template <typename T> T fobj(T, T); // arguments are copiedtemplate <typename T>T fref(const T&, const T&); // reference argumentsint main(){string s1("a value");const string s2("another value");fobj(s1, s2); // ok: calls f(string, string), const is ignoredfref(s1, s2); // ok: non const object s1 converted to const referenceint a[10], b[42];fobj(a, b); // ok: calls f(int*, int*)fref(a, b); // error: array types don't match; arguments aren't converted to pointersreturn 1;}
當形參為引用時,數組不能轉換為指標。
21、類型轉換的限制只適用於類型為模板形參的那些實參。普通類型定義的形參可以使用常規轉換。
樣本
template <class Type> Type sum(const Type &op1, int op2){return op1 + op2;}int main(){double d = 3.14;string s1("hiya"), s2(" world");sum(1024, d); // ok: instantiates sum(int, int), converts d to intsum(1.4, d); // ok: instantiates sum(double, int), converts d to intsum(s1, s2); // error: s2 cannot be converted to intreturn 1;}
22、可以使用函數模板對函數指標進行初始化或賦值。
樣本
template <typename T> int compare(const T&, const T&);// pf1 points to the instantiation int compare (const int&, const int&)int main(){int (*pf1) (const int&, const int&) = compare;return 1;}