項目地址
課程內容
- Part1 C++模板簡介(An Introduction to C++ Template)
- Part2 泛型編程(Generic Programming)
- Part3 容器(Containers)
- Part4 一些進階問題(Some Advanced Topic)
Part1 C++模板簡介
- C++模板概觀(Overview)
- C++函數模板(Function Template)
- C++類模板(Class Template)
- C++操作符重載(Operator Overloading)
C++模板概觀(1)
- 模板(Templates)是C++的一種特性,允許函數或類(對象)通過泛型(generic types)的形式表現或運行
- 模板可以使得函數或類在對應不同的型別(types)的時候正常工作,而無需為每一個型別都寫一份代碼
- 一個簡單的例子:
- 如果要寫一個取兩個數中較大值的函數Max,在不使用模板的情況下,我們不得不針對不同的型別(比如int,long,char)提供每一種型別的重載:
int Max(int a, int b)
{
return (a>b)?a:b;
}
long Max(long a, long b)
{
return (a>b)?a:b;
}
char Max(char a, char b)
{
return (a>b)?a:b;
}
C++模板概觀(2)
- 一個簡單的例子(續)
- 如果使用模板,則可以省去一堆亢余代碼,從而將函數原型縮減到非常簡介的表達:
template <typename T> T Max(T a, T b)
{
return (a>b)?a:b;
}
- C++主要有兩種類型的模板:
- 類模版(Class template):使用泛型參數的類(classes with generic parameters)
- 函數模板(Function template):使用泛型參數的函數(functions with generic parameters)
C++模板概觀(3)
- 模板實例化
- 模板的聲明(declaration)其實并未給出一個函數或類的完全定義(definition),只是提供了一個函數或類的語法框架(syntactical skeletion)
- 實例化是指從模板構建出一個真正的函數或類的過程,比如
template <typename T> struct Object {...};
可以用來構建諸如Object<int>,Object<char>,Object<int>,Object<MyClass>等等不同型別的具體事例 - 實例化有兩種類型:
- 顯式實例化-在代碼中明確指定要針對哪種型別進行實例化
- 隱式實例化-在首次使用時根據具體情況使用一種合適的型別進行實例化
C++函數模板(1)
- 什么是函數模板?
- 函數模板是參數化的一族函數(a family of function)
- 通過函數模板,可以定義一系列函數,這些函數都基于同一套代碼,但是可以作用在不同型別的參數上
template <typename T>
inline T Max(
const T& a,
const T& b)
{
return (a > b) ? a:b;
}
- 定義函數模板
- 定義一個函數模板,返回兩數中較大的那個,該函數有兩個參數:(a,b)
- 參數型別未定,以模板參數T表示
- 模板參數由關鍵typename引入
C++函數模板(2)
- 定義函數模板(續)
- 也可以使用class替代typename來定義型別參數
template <class T> inline T Max(const T& a, const T& b) {
...}
- 從語法上講使用class和使用typename沒有區別
- 但從語義上,class可能會導致誤區,即只有類才能作為型別參數;而事實上T所表達的意思不僅僅只針對類,任何型別都可以
- 請盡量使用typename!
- class可以取代typename,但struct卻不可以,以下寫法語法上是錯誤的:
//this is wrong!!!
template <struct T> inline T Max(const T& a, const T& b)
{
...
}
C++函數模板(3)
- 模板函數的使用
- 調用Max,用int,float,以及std::wstring作為模板參數替換T
- 對于不同的型別,都從模板實例化出不同的函數實體
- 但是不可以使用不同型別的參數來調用Max,因為編譯器在編譯時已經知道Max函數需要傳遞的型別
int i = 7, j = 30;
_tprintf(TEXT("Max(i,j) = %d\n"), Max(i,j));
double f = -1.8, g = -0.9;
_tprintf(TEXT("Max(f,g) = %f\n"), Max(f,g));
std::wstring s1 = TEXT("mathematics"), s2 = TEXT("math");
_tprintf(TEXT("Max(s1,s2) = %s\n"), Max(s1,s2).c_str());
Max(i,f);//compile error: template parameter 'T' is ambiguous
C++函數模板(4)
- 模板實例化
- 用具體型別替代模板參數T的過程叫做實例化(instantiation);從而產生了一個模板事例
- 一旦使用函數模板,這種實例化過程便由編譯器自動觸發的,不需要額外去請求模板實例化
- 如果實例化一種型別,而該型別內部并不支持函數所使用的操作,那么就會導致一個編譯錯誤,如下所示:
- std::complex并沒有重載“>”,也就是說改型別并不支持使用“>”比較大小,而Max函數使用“>”來判斷c1,c2的大小,所以無法通過Max(c1,c2)得到預期的結果
std::complex<int> c1(1,2), c2(15,16); //編譯錯誤!
C++函數模板(5)
- 結論:模板被編譯了兩次
- 沒有實例化之前,檢查模板代碼本身是否有語法錯誤
- 實例化期間,檢查對模板代碼的調用是否合法
C++函數模板(6)
參數推導
模板參數是有傳遞給模板函數的實參決定的
-
不允許自動型別轉換:每個T必須嚴格匹配!
Max(1,2) //OK:兩個實參的型別都是int
Max(1,2.0) //ERROR:第一個參數型別是int,第二個參數型別是double- 一般有兩種處理這種錯誤的方法:
- 用static_cast或強制轉換參數型別以使兩者匹配
Max(static_cast<double>(1), 2.0) - 顯式指定T的型別
Max<double>(1,2.0)
- 用static_cast或強制轉換參數型別以使兩者匹配
- 一般有兩種處理這種錯誤的方法:
C++函數模板(7)
- 函數模板重載
- 函數模板也可以像普通函數一樣被重載
- 非模板函數可以和同名的模板函數共存
- 編譯器通過函數模板參數推導來決定使用調用哪個重載
//普通函數
//1
inline int const& Max(const int const& a, const int const& b)
//2
template <typename T>
inline T const& Max(const T const& a, const T const& b)
//3
template <typename T>
inline T const& Max(const T const& a, const T const& b, const T const& c)
C++函數模板(8)
函數模板重載(續)
- Max(7, 42, 68):調用接受三個參數的模板 ————3
- Max(7.0, 42.0):調用Max<double> (參數推導) ————2
- Max('a', 'b'):調用Max<char> (參數推導) ————2
- Max(7, 42):調用非模板函數,參數型別為int ————1 其他因素都相同的情況下,重載裁決過程調用非模板函數,而不是從模板產生實例
- Max<>(7, 42):調用Max<int> (參數推導) ————2 允許空模板參數列表
- Max<double>(7, 42): 調用Max<double> (無需參數推導) ————2
- Max('a', 42.7):調用非模版函數,參數型別為int ————1 對于型別不同的 參數只能調用非模版函數(char型別'a'和double型別42.7都將轉化為int型別)
C++函數模板(9)
- 總結
- 對于不同的實參型別,模板函數定義了一族函數
- 當傳遞模板實參的時候,函數模板依據實參的型別進行實例化
- 可以顯式指定模板的實參型別
- 函數模板可以重載
- 當重載函數模板時,將改變限制在:顯式指定模板參數
- 所有的重載版本的聲明必須位于它們被調用的位置之前