泛型編程


目錄

  1. 函數模板

    1.1 函數模板的來源

    1.2 函數模板的定義

    1.3 函數模板的特點

    1.4 函數模板的語法規則

    1.5 函數模板的調用方式

    1.6 多參數的函數模板

    1.7 函數模板與函數重載

    1.8 函數模板的實現機制(兩次編譯)

  2. 類模板

    2.1 類模板的概念和意義

    2.2 類模板的語法規則

    2.3 類模板的實現機制(兩次編譯)

    2.4 類模板在工程應用中的推薦寫法

    2.5 類模板的特化(包括函數模板的特化)

    2.6 關於特化的一些問題 

   顧名思義泛型編程就是使用“泛型”的思想去寫代碼,這里的“泛型”指的是將數據類型作為參數傳遞(類型參數化);換言之 泛型編程 是 一種不考慮具體數據類型的編程方式,其典型代表就是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:使用宏代碼塊
 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種方法都可以實現功能,但是它們各自都有缺點。

    定義宏代碼塊

      優點:代碼復用,適合所有的類型;

      缺點:缺少類型檢查;

    定義函數

      優點:真正的函數調用,有類型檢查;

      缺點:根據類型重復定義函數,無法代碼復用;

  那么,有沒有一種方法可以同時擁有上述2種方法的優點(1. 代碼復用,適合所有類型; 2. 類型檢查)呢?--- 當然有了,那就是 函數模板

2、函數模板的定義

  所謂函數模板,實際上是建立一個通用函數,它所用到的數據類型(包括返回值類型、形參類型、局部變量類型)可以不具體指定,而是用一個虛擬的類型來代替(實際上是用一個標識符來占位);當發生函數調用時再根據傳入的實參來自動推導出真正的數據類型。

  在函數模板中,數據的值和類型都被參數化了,發生函數調用時編譯器會根據傳入的實參來推演形參的值和類型。換個角度說,函數模板除了支持值的參數化,還支持類型的參數化

  只要定義了函數模板,就可以將類型參數用於函數定義和函數聲明了。

3、函數模板的特點

  1. 在函數定義時可以不指明具體的數據類型;--- 函數定義

  2. 一種特殊的函數,可用不同的類型進行調用;---  函數調用

  3. 看起來與普通函數很相似,區別是類型被參數化(當發生函數調用時,數據的類型可以通過參數來傳遞,編譯器根據傳入的實參自動推斷數據類型。--- 參數傳遞

4、函數模板的語法規則

  template <typename T>

   template <typename T, typename T1, typename T2, ...>  多參數的函數模板, T 第一個類型參數,T1 是第二個類型參數...

  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 = 111 int b = 2;
12 Swap(a, b);         // 自動推導調用方式
13 Swap<int>(a, b);    // 具體類型顯示調用
14 
15 // 測試 2
16 int a = 317 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 T1, typename T2, typename T3, ...>  多參數的類模板聲明, T1 第一個類型參數,T2 是第二個類型參數... 

  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  * /
函數模板特化與函數重載
 
        
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM