目錄
顧名思義泛型編程就是使用“泛型”的思想去寫代碼,這里的“泛型”指的是將數據類型作為參數傳遞(類型參數化);換言之 泛型編程 是 一種不考慮具體數據類型的編程方式,其典型代表就是STL(Standard Template Library 標准模板庫)。
1. 如果將泛型編程的思想應用於函數中,就產生了函數模板(通用函數);
2. 同理,將泛型編程的思想應用於類中,就會產生類模板(通用類);
接下來就分別介紹這兩種技術:函數模板、類模板。
函數模板(Function Template)
1、函數模板的來源
為了更加深刻的理解函數模板,我們可以用一個例子說明情況。比如,現在要交換兩個變量的值,怎么做?就目前來看有2種方法,分別是 使用宏代碼塊 和 使用函數定義(函數重載);

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 #define SWAP(t, a, b) \ 7 do \ 8 { \ 9 t c = a; \ 10 a = b; \ 11 b = c; \ 12 }while(0) 13 14 int main() 15 { 16 int a = 10; 17 int b = 11; 18 19 SWAP(int, a, b); 20 21 cout << "a = " << a << endl; 22 cout << "b = " << b << endl; 23 24 double m = 12; 25 double n = 13; 26 27 SWAP(double, m, n); 28 29 cout << "m = " << m << endl; 30 cout << "n = " << n << endl; 31 32 string s1 = "c++"; 33 string s2 = "python"; 34 35 SWAP(string, s1, s2); 36 37 cout << "s1 = " << s1 << endl; 38 cout << "s2 = " << s2 << endl; 39 40 return 0; 41 42 } 43 /** 44 * a = 11 45 * b = 10 46 * m = 13 47 * n = 12 48 * s1 = python 49 * s2 = c++ 50 */

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 void Swap(int& a, int& b) 7 { 8 int c = a; 9 a = b; 10 b = c; 11 } 12 13 void Swap(double& a, double& b) 14 { 15 double c = a; 16 a = b; 17 b = c; 18 } 19 20 void Swap(string& a, string& b) 21 { 22 string c = a; 23 a = b; 24 b = c; 25 } 26 27 28 int main() 29 { 30 int a = 10; 31 int b = 11; 32 33 Swap(a, b); 34 35 cout << "a = " << a << endl; 36 cout << "b = " << b << endl; 37 38 double m = 12; 39 double n = 13; 40 41 Swap(m, n); 42 43 cout << "m = " << m << endl; 44 cout << "n = " << n << endl; 45 46 string s1 = "c++"; 47 string s2 = "python"; 48 49 Swap(s1, s2); 50 51 cout << "s1 = " << s1 << endl; 52 cout << "s2 = " << s2 << endl; 53 54 return 0; 55 56 } 57 /** 58 * a = 11 59 * b = 10 60 * m = 13 61 * n = 12 62 * s1 = python 63 * s2 = c++ 64 */
通過案列可知,這2種方法都可以實現功能,但是它們各自都有缺點。
定義宏代碼塊
優點:代碼復用,適合所有的類型;
缺點:缺少類型檢查;
定義函數
優點:真正的函數調用,有類型檢查;
缺點:根據類型重復定義函數,無法代碼復用;
那么,有沒有一種方法可以同時擁有上述2種方法的優點(1. 代碼復用,適合所有類型; 2. 類型檢查)呢?--- 當然有了,那就是 函數模板。
2、函數模板的定義
所謂函數模板,實際上是建立一個通用函數,它所用到的數據類型(包括返回值類型、形參類型、局部變量類型)可以不具體指定,而是用一個虛擬的類型來代替(實際上是用一個標識符來占位);當發生函數調用時再根據傳入的實參來自動推導出真正的數據類型。
在函數模板中,數據的值和類型都被參數化了,發生函數調用時編譯器會根據傳入的實參來推演形參的值和類型。換個角度說,函數模板除了支持值的參數化,還支持類型的參數化。
只要定義了函數模板,就可以將類型參數用於函數定義和函數聲明了。
3、函數模板的特點
1. 在函數定義時可以不指明具體的數據類型;--- 函數定義
2. 一種特殊的函數,可用不同的類型進行調用;--- 函數調用
3. 看起來與普通函數很相似,區別是類型被參數化(當發生函數調用時,數據的類型可以通過參數來傳遞,編譯器根據傳入的實參自動推斷數據類型)。--- 參數傳遞
4、函數模板的語法規則
template <typename T>
template 關鍵字用於聲明開始進行泛型編程;
typename 關鍵字用於聲明泛指類型;

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template < typename T > 7 void Swap(T& a, T& b) 8 { 9 T c = a; 10 a = b; 11 b = c; 12 } 13 14 template < typename T > 15 void Sort(T a[], int len) 16 { 17 for(int i=0; i<len-1; i++) 18 { 19 for(int j=i+1; j<len; j++) 20 { 21 if( a[i] > a[j] ) 22 { 23 Swap(a[i], a[j]); 24 } 25 } 26 } 27 } 28 29 template < typename T > 30 void Print(T a[], int len) 31 { 32 for(int i=0; i<len; i++) 33 { 34 cout << a[i] << ", "; 35 } 36 37 cout << endl; 38 } 39 40 int main() 41 { 42 int a[5] = {5, 3, 2, 4, 1}; 43 44 cout << "排序前:"; 45 Print(a, 5); 46 Sort(a, 5); 47 cout << "排序后:"; 48 Print(a, 5); 49 50 string s[5] = {"Java", "C++", "Python", "Html", "Matlab"}; 51 52 cout << "排序前:"; 53 Print(s, 5); 54 Sort(s, 5); 55 cout << "排序后:"; 56 Print(s, 5); 57 58 return 0; 59 } 60 /** 61 * 排序前:5, 3, 2, 4, 1, 62 * 排序后:1, 2, 3, 4, 5, 63 * 排序前:Java, C++, Python, Html, Matlab, 64 * 排序后:C++, Html, Java, Matlab, Python, 65 */
5、函數模板的調用方式
!!!發生函數調用時,函數模板會根據 實參 對 參數類型 進行推導,但無法自動推導返回值類型;
!!!函數模板本身不允許隱式類型轉換
1. 自動推導調用方式(實參類型與泛指類型必須嚴格匹配,不能進行隱式類型轉換);
2. 具體類型顯示調用(顯示的指定數據類型,可以進行隱式類型轉換);
3. 自動推導 + 顯示調用 二者結合(在多參數的函數模板中使用);
1 template <typename T> 2 void Swap(T& a, T& b) 3 { 4 T c = a; 5 a = b; 6 b = c; 7 } 8 9 // 測試 1 10 int a = 1; 11 int b = 2; 12 Swap(a, b); // 自動推導調用方式 13 Swap<int>(a, b); // 具體類型顯示調用 14 15 // 測試 2 16 int a = 3; 17 float b = 4.5; 18 Swap(a, b); // error,a, b 數據類型不一致,若此時進行函數調用,會產生二義性(如 void Swap(int a, int b); void Swap(float a, float b); ) 19 Swap<float>(a, b); // 若指定了數據類型為 float,即 T = float,在函數調用過程中(void Swap(float a, float b); ),a 進行了隱式類型轉換
6、多參數的函數模板
可以從左向右部分指定類型參數,工程中將返回值類型作為第一個類型參數(這樣方便進行自動推導);

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2, typename T3 > 8 T1 Add(T2 a, T3 b) 9 { 10 return static_cast<T1>(a + b); 11 } 12 13 int main() 14 { 15 // T1 = int, T2 = double, T3 = double 16 int r1 = Add<int>(0.5, 0.8); 17 18 // T1 = double, T2 = float, T3 = double 19 double r2 = Add<double, float>(0.5, 0.8); 20 21 // T1 = float, T2 = float, T3 = float 22 float r3 = Add<float, float, float>(0.5, 0.8); 23 24 cout << "r1 = " << r1 << endl; // r1 = 1 25 cout << "r2 = " << r2 << endl; // r2 = 1.3 26 cout << "r3 = " << r3 << endl; // r3 = 1.3 27 28 return 0; 29 }
7、函數模板與函數重載
調用規則:
1. 函數模板可以像普通函數一樣被重載;
2. C++編譯器優先考慮普通函數;
3. 如果函數模板可以產生一個更好的匹配,那么選擇模板;
4. 可以通過 空模板實參列表 的語法限定編譯器只通過模板匹配 。// func<>(a,b);
!!!函數模板本身不允許隱式類型轉換;
!!!函數模板支持完全特化(在類模板中介紹)
!!!在重載函數模板時,優先選擇函數模板特化,其次才是函數重載。

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 7 template < typename T > 8 T Max(T a, T b) 9 { 10 cout << "T Max(T a, T b)" << endl; 11 12 return a > b ? a : b; 13 } 14 15 int Max(int a, int b) 16 { 17 cout << "int Max(int a, int b)" << endl; 18 19 return a > b ? a : b; 20 } 21 22 template < typename T > 23 T Max(T a, T b, T c) 24 { 25 cout << "T Max(T a, T b, T c)" << endl; 26 27 return Max(Max(a, b), c); 28 } 29 30 int main() 31 { 32 cout << Max(1, 2) << endl; // 普通函數 Max(int, int) 33 cout << Max<>(1, 2) << endl; // 函數模板 Max<int>(int, int) 34 cout << Max(3.0, 4) << endl; // 普通函數 Max(int, int) 35 cout << Max(3.0, 4.0) << endl; // 函數模板 Max<double>(double, double) 36 cout << Max(5.0, 6.0, 7.0) << endl; // 函數模板 Max<double>(double, double, double) 37 38 return 0; 39 } 40 /** 41 * int Max(int a, int b) 42 * 2 43 * T Max(T a, T b) 44 * 2 45 * int Max(int a, int b) 46 * 4 47 * T Max(T a, T b) 48 * 4 49 * T Max(T a, T b, T c) 50 * T Max(T a, T b) 51 * T Max(T a, T b) 52 * 7 53 */
8、函數模板的實現機制
1. 編譯器從函數模板通過具體類型產生不同的函數;
2. 編譯器會對函數模板進行兩次編譯;
(1)對模板代碼本身進行編譯;
(2)對參數替換后的代碼進行編譯;

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 //Test(const Test&); 9 public: 10 Test() 11 { 12 } 13 }; 14 15 template < typename T > 16 void Swap(T& a, T& b) 17 { 18 T c = a; // 當T=Test時,第二次編譯就會調用拷貝構造函數(private),此時編譯失敗 19 a = b; 20 b = c; 21 } 22 23 typedef void(FuncI)(int&, int&); 24 typedef void(FuncD)(double&, double&); 25 typedef void(FuncT)(Test&, Test&); 26 27 int main() 28 { 29 FuncI* pi = Swap; // 編譯器自動推導 T 為 int 30 FuncD* pd = Swap; // 編譯器自動推導 T 為 double 31 FuncT* pt = Swap; // 編譯器自動推導 T 為 Test 32 33 cout << "pi = " << reinterpret_cast<void*>(pi) << endl; // pi = 0x400a0a 34 cout << "pd = " << reinterpret_cast<void*>(pd) << endl; // pd = 0x400a37 35 cout << "pt = " << reinterpret_cast<void*>(pt) << endl; // pt = 0x400a70 36 37 return 0; 38 }
類模板(Class Template)
1、 類模板的概念和意義
在C++ 中,將模板(泛型)的思想應用於類,使得類的實現不在關注數據元素的具體類型,而只關注類所需要實現的功能,這就是c++中的的類模板。總之,類模板就是以相同的方式去處理不同類型的數據。
在STL中有很多類(數組類、鏈表類、Stack 類、Queue 類等),這些類主要用於存儲和組織數據元素,且這些類的實現與具體的數據類型無關;這樣做的好處是將統一的算法應用在不同的數據類型之間。那么,在STL中,這項技術是如何實現的?--- 通過 類模板 實現。
由此可見,類模板非常適合編寫數據結構的相關代碼。
2、類模板的語法規則
template <typename T>
template 關鍵字用於聲明開始進行泛型編程;
typename 關鍵字用於聲明泛指類型;
聲明的泛指類型 T 可以出現在類模板的任意地方;(以上內容與函數模板一樣)
!!!在類模板創建對象時,必須顯示指定具體類型,不能自動推導;可以通過使用具體類型<Type>創建對象。

1 #include <iostream> 2 3 using namespace std; 4 5 // 定義一個模板類 6 template <typename T> 7 class Demo 8 { 9 private: 10 T v; 11 public: 12 Demo(T v) 13 { 14 cout << "Demo(T v)" << endl; 15 this->v = v; 16 } 17 18 T Get() 19 { 20 return v; 21 } 22 }; 23 24 int main() 25 { 26 // 使用類模板創建對象時,必須顯示指定類型!!! 27 Demo<int> d1(10); 28 cout << d1.Get() << endl; 29 30 Demo<string> d2("template test"); 31 cout << d2.Get() << endl; 32 33 return 0; 34 } 35 /** 36 * Demo(T v) 37 * 10 38 * Demo(T v) 39 * template test 40 */
3、 類模板的實現機制(兩次編譯)
編譯器對類模板的處理方式和函數模板相同,即使用相同的方式處理不同的類型,都會經過兩次編譯。
1. 在聲明的地方對類模板代碼本身進行編譯;
2. 在使用的地方對參數替換后的代碼進行編譯;(!!!部分編譯,僅僅對所調用的代碼進行編譯)
怎么證明類模板是使用兩次編譯的?
為了說明問題,可以使用字符串測試。當 兩個字符串直接相加時,其實是對這兩個字符串進行拼接,如”string“ + ”666“ = ”string666“;但是,當字符串與數字相加時,程序在編譯時就會報錯,因為在string類中沒有重載這種操作。
測試流程:
首先,定義一個類模板,接下來用類模板分別創建對象;
1 template < typename T1, typename T2 > 2 class Demo 3 { 4 public: 5 Demo() 6 { 7 cout << "Demo()" << endl; 8 } 9 10 T1 joint(T1 a, T2 b) 11 { 12 cout << "T1 joint(T1 a, T2 b)" << endl; 13 return a + b; 14 } 15 };
若使用如下測試語句,程序正常執行:
1 // 第二次編譯,編譯對象d1對應的構造函數,此時,T1=String,T2=String 2 Demo<string, string> d1; 3 // 第二次編譯,編譯對象d1對應的成員函數 T1 joint(T1 a, T2 b){..} 4 cout << d1.joint("Test", "_string") << endl; 5 6 /** 7 * 運行結果 8 * Demo() 9 * T1 joint(T1 a, T2 b) 10 * Test_string 11 */
若使用如下測試語句,程序則報錯:
1 // 第二次編譯,編譯對象d2對應的構造函數,此時,T1=String,T2=int 2 Demo<string, int> d2; 3 /* 4 第二次編譯,編譯對象d2對應的成員函數 5 T1 joint(T1 a, T2 b) 6 { 7 cout << "T1 joint(T1 a, T2 b)" << endl; 8 return a + b; // !!!報錯 9 } 10 */ 11 cout << d2.joint("Test", 3) << endl;
報錯原因:String 與 int 不能直接相加,需要重載+操作符
修改錯誤,添加如下代碼:
1 // 如 ”abc“ + 3 = ”abcabcabc“ 2 string operator+(string &l, int r) 3 { 4 string ret = ""; 5 6 while(r) 7 { 8 ret += l; 9 r--; 10 } 11 12 return ret; 13 }
程序可以正常運行,運行結果為:
最后附上完整代碼:

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template < typename T1, typename T2 > 7 class Demo 8 { 9 public: 10 Demo() 11 { 12 cout << "Demo()" << endl; 13 } 14 15 T1 joint(T1 a, T2 b) 16 { 17 cout << "T1 joint(T1 a, T2 b)" << endl; 18 return a + b; 19 } 20 }; 21 22 string operator+(string &l, int r) 23 { 24 string ret = ""; 25 26 while(r) 27 { 28 ret += l; 29 r--; 30 } 31 32 return ret; 33 } 34 35 36 int main() 37 { 38 Demo<string, string> d1; 39 40 cout << d1.joint("Test", "_string") << endl; 41 42 Demo<string, int> d2; 43 44 cout << d2.joint("Test", 3) << endl; 45 46 return 0; 47 } 48 49 /** 50 * 運行結果: 51 * Demo() 52 * joint(T1 a, T2 b) 53 * Test_string 54 * Demo() 55 * joint(T1 a, T2 b) 56 * TestTestTest 57 */
4、類模板在工程應用中的推薦寫法
1. 類模板必須在頭文件中定義(.hpp文件);
2. 類模板不能分開實現在不同的文件中(聲明與定義必須在同一個文件中);
3. 類模板外部定義的成員函數需要加上模板 <> 聲明;
注:以上三條規則不是 C++ 和編譯器的一部分,只是工程應用里習慣這樣做;這樣做后,代碼可維護性、擴展性都會變好,因此建議遵守這三條規則;

1 // 頭文件 test.hpp 2 #ifndef _TEST_H 3 #define _TEST_H 4 5 #include <iostream> 6 #include <string> 7 8 using namespace std; 9 10 template < typename T1, typename T2 > 11 class Demo 12 { 13 public: 14 Demo(); 15 T1 joint(T1 a, T2 b); 16 }; 17 18 template < typename T1, typename T2 > 19 Demo<T1, T2>::Demo() 20 { 21 cout << "Demo()" << endl; 22 } 23 24 template < typename T1, typename T2 > 25 T1 Demo<T1, T2>::joint(T1 a, T2 b) 26 { 27 cout << "T1 joint(T1 a, T2 b)" << endl; 28 return a + b; 29 } 30 31 // 全局函數 32 string operator+(string &l, int r) 33 { 34 string ret = ""; 35 36 while(r) 37 { 38 ret += l; 39 r--; 40 } 41 42 return ret; 43 } 44 45 #endif 46 47 // main.cpp 48 49 #include <iostream> 50 #include <string> 51 #include "test.hpp" 52 53 using namespace std; 54 55 int main() 56 { 57 Demo<string, string> d1; 58 59 cout << d1.joint("Test", "_string") << endl; 60 61 Demo<string, int> d2; 62 63 cout << d2.joint("Test", 3) << endl; 64 65 return 0; 66 } 67 68 /** 69 * 運行結果: 70 * Demo() 71 * joint(T1 a, T2 b) 72 * Test_string 73 * Demo() 74 * joint(T1 a, T2 b) 75 * TestTestTest 76 */
5、類模板的特化
“特化” 由兩部分組成:
1. 部分特化:任何針對模版參數進一步進行條件限制設計的特化版本;(模板條件<>中必須有 泛指類型T,可以與其它類型一起使用)
2. 完全特化:針對所有的模版參數進行特化;(模板條件<>中都是具體類型,不包括 泛指類型T)
類模板“特化”的注意事項:
1. “特化” 就是指定類模板的特定實現;
2. “特化” 是根據類型參數分開實現類模板,本質還是同一個類模板;
3. “特化”類模板的使用方式是統一的,必須顯示的指定每一個類型參數。
類模板的特化:
1 // 類模板 2 template<typename T1, typename T2> 3 class Demo{}; 4 5 // 部分特化 6 template<typename T, typename int> 7 class Demo<T, int>{}; 8 9 template<typename T> 10 class Demo<T, T>{}; 11 12 template<typename T1, typename T2> 13 class Demo<T1*, T2*>{}; 14 15 // 完全特化 16 template<> 17 class Demo<int, int>{}; 18 19 template<> 20 class Demo<const char*, int>{};
補充:函數模板的特化(只支持完全特化):
// 函數模板 template <typename T> void Equal(T a, T b){}; // 函數模板的完全特化 template <> void Equal<double>(double a, double b){}; // !!!函數模板不支持部分特化
特化案列:

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Demo 9 { 10 public: 11 void joint(T1 a, T2 b) 12 { 13 cout << "類模板:void joint(T1 a, T2 b)" << endl; 14 cout << a << b << endl; 15 } 16 }; 17 18 template 19 <typename T1, typename T2 > 20 class Demo<T1*, T2*> // 關於指針的特化實現 21 { 22 public: 23 void joint(T1* a, T2* b) 24 { 25 cout << "部分指針特化:void joint(T1* a, T2* b)" << endl; 26 cout << *a << *b << endl; 27 } 28 }; 29 30 template 31 <typename T> 32 class Demo<T, T> // 當 Demo 類模板的兩個類型參數完全相同時,使用這個實現 33 { 34 public: 35 void joint(T a, T b) 36 { 37 cout << "部分特化:void joint(T a, T b)" << endl; 38 cout << a << b << endl; 39 } 40 }; 41 42 template 43 <> 44 class Demo<char*, char*> // 當T1 = char*, T2 = char*,使用這個實現 45 { 46 public: 47 void joint(const char* a, const char* b) 48 { 49 cout << "完全指針特化:void joint(char* a, char* b)" << endl; 50 cout << a << b << endl; 51 } 52 }; 53 54 template 55 <> 56 class Demo<string, int> // 當T1 = string, T2 = int,使用這個實現 57 { 58 public: 59 void joint(string a, int b) 60 { 61 string ret = ""; 62 63 while(b) 64 { 65 ret += a; 66 b--; 67 } 68 69 cout << "完全特化:void joint(string a, int b)" << endl; 70 cout << ret << endl; 71 } 72 }; 73 74 75 int main() 76 { 77 Demo<string, string> d1; 78 d1.joint("Test", "_string"); 79 80 Demo<int, int> d2; 81 d2.joint(12, 34); 82 83 Demo<char*, char*> d3; 84 d3.joint("Test", "_char*"); 85 86 Demo<string, int> d4; 87 d4.joint("Test", 3); 88 89 Demo<int*, float*> dd; 90 int a = 13; 91 float b = 14; 92 dd.joint(&a, &b); 93 94 Demo<const char*, string> d; 95 d.joint("12", ".34"); 96 97 return 0; 98 } 99 100 /** 101 * 運行結果: 102 * 部分特化:void joint(T a, T b) 103 * Test_string 104 * 部分特化:void joint(T a, T b) 105 * 1234 106 * 完全指針特化:void joint(char* a, char* b) 107 * Test_char* 108 * 完全特化:void joint(string a, int b) 109 * TestTestTest 110 * 部分指針特化:void joint(T1* a, T2* b) 111 * 1314 112 * 類模板:void joint(T1 a, T2 b) 113 * 12.34 114 * /
由上述案列可知,對象調用對應成員函數的優先順序為:完全特化 > 部分特化 > 類模板
6、關於特化的一些問題
1. 類模板特化與重定義的差異:
重定義:
(1) 重定義必須實現兩個類模板 或者是 一個類模板加上 一個新的類;
(2) 重定義之后,二者沒有任何關系
(3) 使用的時候需要考慮如何選擇的問題(因為重定義之后又多了一個類,所以類模板統一的使用方式被銷毀)
特化:
(1) 特化只是模板的分開實現,本質還是同一個類模板;
(2) 特化是類模板的一部分,而這一部分有自己專有的工作方式;
(3) 類模板特化后,能夠以統一的方式使用類模板和特化類;(與類模板的使用方式相同)
結論:特化與重定義是實現同一功能的兩種不同手段;在使用時,優先選擇特化實現,只有特化解決不了才會重新考慮重定義。

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Demo 9 { 10 public: 11 void joint(T1 a, T2 b) 12 { 13 cout << "類模板:void joint(T1 a, T2 b)" << endl; 14 cout << a << b << endl; 15 } 16 }; 17 18 // 完全特化實現 19 template 20 <> 21 class Demo<string, int> // 當T1 = string, T2 = int,使用這個實現 22 { 23 public: 24 void joint(string a, int b) 25 { 26 string ret = ""; 27 28 while(b) 29 { 30 ret += a; 31 b--; 32 } 33 34 cout << "完全特化:void joint(string a, int b)" << endl; 35 cout << ret << endl; 36 } 37 }; 38 39 // 重定義實現 40 class Demo_redefine 41 { 42 public: 43 void joint(string a, int b) 44 { 45 string ret = ""; 46 47 while(b) 48 { 49 ret += a; 50 b--; 51 } 52 53 cout << "重定義:void joint(string a, int b)" << endl; 54 cout << ret << endl; 55 } 56 }; 57 58 int main() 59 { 60 // 對於同一功能的不同實現方法:特化實現 與 重定義一個新類,優先選擇 特化實現 61 // 特化實現 62 Demo<string, int> d4; 63 d4.joint("Test", 3); 64 // 重定義實現 65 Demo_redefine dr; 66 dr.joint("Test", 3); 67 68 return 0; 69 } 70 71 /** 72 * 運行結果: 73 * 完全特化:void joint(string a, int b) 74 * TestTestTest 75 * 重定義:void joint(string a, int b) 76 * TestTestTest 77 * /
2. 函數模板完全特化 與 函數重載
結論:
(1)函數模板特化 與 函數重載同時存在時,編譯器優先選擇函數重載,其次函數模板特化,最后,函數模板;
(2)工程建議:當需要重載函數模板時,優先考慮使用模板特化;當模板特化無法滿足需求,再使用函數重載!

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 // 函數模板 7 template 8 < typename T > 9 bool Equal(T a, T b) 10 { 11 cout << "函數模板:bool Equal(T a, T b)" << endl; 12 13 return a == b; 14 } 15 16 // 函數模板完全特化 17 template 18 < > 19 bool Equal<double>(double a, double b) 20 { 21 const double delta = 0.00000000000001; 22 double r = a - b; 23 24 cout << "函數模板完全特化:bool Equal<double>(double a, double b)" << endl; 25 26 return (-delta < r) && (r < delta); 27 } 28 29 // 函數重載 30 bool Equal(double a, double b) 31 { 32 const double delta = 0.00000000000001; 33 double r = a - b; 34 35 cout << "函數重載:bool Equal(double a, double b)" << endl; 36 37 return (-delta < r) && (r < delta); 38 } 39 40 int main() 41 { 42 cout << Equal( 1, 1 ) << endl; // Equal<int>(int, int) 43 cout << Equal( 1.1, 1.1 ) << endl; // Equal(double, double) // 編譯器優先選擇普通函數 44 cout << Equal<>( 0.001, 0.001 ) << endl;// Equal<double>(double, double) 45 46 return 0; 47 } 48 // 注:不能直接使用 “=” 判斷兩個浮點數是否相等 49 /** 50 * 運行結果: 51 * 函數模板:bool Equal(T a, T b) 52 * 1 53 * 函數重載:bool Equal(double a, double b) 54 * 1 55 * 函數模板完全特化:bool Equal<double>(double a, double b) 56 * 1 57 * /