一、模板函數實例化:
存在這樣一種函數,它們在行為上是完全一致的,而不同只是函數的參數類型。對於這種類型的函數,我們可以應用C++模板和泛型來幫助我們更優雅和更具技巧性的的解決一些程序設計上的實際問題。如:
1 template <typename T> 2 inline T const& max(T const& a, T const& b) { 3 return a < b ? b : a; 4 }
對於上面的模板函數,我們在實際的應用中可以用任意類型來實例化該模板函數,如:
1 int main() { 2 printf("The max value is %d\n", ::max(4,5)); //int max(int, int) 3 printf("The max value is %f\n", ::max(4.2,5.3)); //float max(float, float) 4 return 0; 5 }
在上面的實例中,C++編譯器會通過調用參數將模板函數實例化,同時返回比較后的結果。當然,對於這樣的方法我們也可以通過宏的方式來實現,如:
#define max(a,b) ((a) < (b)) ? (b) : (a)
相比於模板函數,宏的方式還是存在一些問題的,比如類型不安全。由於這個系列主要介紹的是C++模板,因此對於其他技術就不做更多的贅述了。對於模板函數max,有一些幾點需要注意:
1. 參數a和參數b的類型要保持一致,否則編譯器無法進行正確的類型推演。如:int a = max(4,3.3)。對於這種調用情況,我們可以修改為int a = max(4, (int)3.3),或者修改為int a = max<int>(4,3.3),即顯示的指定模板函數max的類型參數。
2. 對於類型T的實際類型而言,該類型必需提供operator <() 的支持,否則無法成功編譯。對於上面的示例,整型和浮點型均提供了小於操作符的內嵌支持,而對於其它的class而言,則需要給class提供重載小於操作符的方法。如:
1 class MyClass { 2 public: 3 MyClass(int value) : _value(value) { 4 } 5 bool operator< (const MyClass& other) const { //注意這里的操作符重載方法必需是const函數。 6 printf("operator < is call\n"); 7 return _value < other._value; 8 } 9 int myValue() const { return _value; } 10 private: 11 int _value; 12 }; 13 14 int main() 15 { 16 MyClass m1(30), m2(40); 17 printf("The max value is %d\n",::max(m1,m2).myValue()); 18 return 0; 19 }
如果MyClass類沒有提供bool operator< (const MyClass& other) const函數,該段代碼將無法成功編譯。
3. 函數參數傳遞的是引用,而不是傳值。這樣便可以在函數聲明時返回應用類型。否則,如果函數參數是傳值的,如:T const& max(T const a, T const b),那么對於該種情況,由於在比較時將會使用函數內部的兩個臨時變量,即實數a和b在函數內的副本。這樣在函數返回時,如果返回臨時變量的引用后果可想而知。因此可改為:T const max(T const a, T const b)。
4. C++的編譯器會為每一種類型都產生一個實例化函數,而不是僅僅構造出一個函數實例,然后再基於不同的參數類型調用該通用函數。如:
1 template <typename T> 2 inline T const& max(T const& a, T const& b) { 3 static int i = 0; 4 printf("The value of i is %d\n",i++); 5 return a < b ? b : a; 6 } 7 8 class MyClass { 9 public: 10 MyClass(int value) : _value(value) { 11 } 12 bool operator< (const MyClass& other) const { 13 return _value < other._value; 14 } 15 int myValue() const { return _value; } 16 private: 17 int _value; 18 }; 19 20 int main() 21 { 22 ::max(4,5); //for max<int> 23 ::max(5,6); //for max<int> 24 ::max(5.3,5.4); //for max<float> 25 ::max(5.3,5.4); //for max<float> 26 MyClass m1(30), m2(40); 27 ::max(m1,m2); //for max<MyClass> 28 getch(); 29 return 0; 30 } 31 //The value of i is 0 32 //The value of i is 1 33 //The value of i is 0 34 //The value of i is 1 35 //The value of i is 0
模板函數max中的變量i是靜態變量,該變量只會在函數第一次被調用時初始化一次。從輸出結果可以看出,該變量被初始化三次,每一次都是基於不同類型的模板函數實例化。由此可見,max模板函數確實在代碼段中被生成三份。
二、重載函數模板:
和普通的函數重載一樣,模板函數也可以實現函數重載的功能。如:
1 #include <stdio.h> 2 #include <conio.h> 3 4 inline int const& max(int const& a, int const& b) { 5 printf("int const& max(int const& a, int const& b) is called.\n"); 6 return a < b ? b : a; 7 } 8 9 template <typename T> 10 inline T const& max(T const& a, T const& b) { 11 printf("T const& max(T const& a, T const& b) is called.\n"); 12 return a < b ? b : a; 13 } 14 15 template <typename T> 16 inline T const& max(T const& a, T const& b, T const& c) { 17 printf("T const& max(T const& a, T const& b, T const& c) is called.\n"); 18 return ::max(::max(a,b),c); 19 } 20 21 class MyClass { 22 public: 23 MyClass(int value) : _value(value) { 24 } 25 bool operator< (const MyClass& other) const { 26 printf("operator < is call\n"); 27 return _value < other._value; 28 } 29 int myValue() const { return _value; } 30 private: 31 int _value; 32 }; 33 34 int main() { 35 ::max(5,43,68); //三個參數的max<int> 36 printf("------------------------------------\n"); 37 ::max('a','b'); //兩個參數的max<char> 38 ::max(7,54); //int參數類型的非模板函數。 39 ::max<>(7,54); //兩個參數的max<int> 40 ::max('a',43.2); //int參數類型的非模板函數。 41 getch(); 42 return 0; 43 } 44 // T const& max(T const& a, T const& b, T const& c) is called. 45 // int const& max(int const& a, int const& b) is called. 46 // int const& max(int const& a, int const& b) is called. 47 // ------------------------------------ 48 // T const& max(T const& a, T const& b) is called. 49 // int const& max(int const& a, int const& b) is called. 50 // T const& max(T const& a, T const& b) is called. 51 // int const& max(int const& a, int const& b) is called.
從上面的輸出可以看出,可以了解到以下幾點:
1. 非模板函數和模板函數可以同名,在調用時可以一起進行函數重載的推演。
2. 編譯器在進行函數重載的推演時,首選非模板類型的函數。
3. 通過max<>方式通知編譯器,在推演時只考慮模板函數。
4. 由於模板函數在實例化的過程中,不會自動完成任何形式的類型轉換,因此對於最后一個調用函數,由於兩個參數的類型不一致,而普通函數max通過類型自動轉換((int)'a',(int)43.2)可以覆蓋該調用,所以編譯器選擇了該普通函數。
