普通重載函數可以通過函數參數進行推演,並由編譯器選定最適合的重載函數作為候選函數。與此類似,模板函數可以通過函數參數的類型推演出該函數模參的實際類型。C++的編譯器在完成類型推演的過程中有以下一些技巧和注意事項,這裡我們將儘可能的列出最為常用的規則,並給出相應的樣本以便於理解。
1. 最基本的模板函數類型推演。見以下程式碼範例、關鍵性注釋和輸出結果。
1 #include <stdio.h> 2 #include <typeinfo.h> 3 4 template<typename T> 5 void func1(T* t) { 6 printf("The type is %s\n",typeid(t).name()); 7 } 8 9 template<typename T,int N>10 void func2(T(&)[N]) {11 printf("The type of T is %s, N equals to %d.\n",typeid(T).name(),N);12 }13 14 template<typename T1,typename T2,typename T3>15 void func3(T1 (T2::*)(T3*)) {16 printf("The type of T1 is %s\n",typeid(T1).name());17 printf("The type of T2 is %s\n",typeid(T2).name());18 printf("The type of T3 is %s\n",typeid(T3).name());19 }20 21 template<int N>22 class TemplateTest {23 public:24 typedef int INT;25 void f(int) {}26 };27 28 template<int N>29 void func4(void (TemplateTest<N>::*p)(typename TemplateTest<N>::INT) ) {30 printf("N equals to %d\n",N);31 }32 33 class Test {34 public:35 void f(double*) {}36 };37 38 int main() {39 int** pp = NULL;40 func1(pp); //T的類型為int*41 42 bool b[100];43 func2(b); //T的類型為bool,N的值是10044 45 func3(&Test::f); //T1:void,T2:class Test,T3:double。46 47 func4(&TemplateTest<200>::f);48 return 0;49 }50 //The type is int * *51 //The type of T is bool, N equals to 100.52 //The type of T1 is void53 //The type of T2 is class Test54 //The type of T3 is double55 //N equals to 200
2. 如果函數參數為T&(參考型別),其模參類型仍為T。
1 #include <stdio.h> 2 #include <typeinfo.h> 3 4 template<typename T> 5 void func(T& s) { 6 printf("The type is %s\n",typeid(s).name()); 7 } 8 9 int main() {10 int k = 0;11 func(k);12 return 0;13 }14 //The type is int
從輸出結果可以看出,T的實際類型仍為int,而非int&。
3. 如果函數參數為參考型別,而函數的實參則為數組或函數類型,那麼將不會進行任何隱式轉換。如參數不為參考型別,在此種情況下將會產生從數組到指標和函數到函數指標的隱式類型轉換。
1 #include <stdio.h> 2 #include <typeinfo.h> 3 4 template<typename T> 5 void func(T const& s) { 6 printf("The type is %s\n",typeid(s).name()); 7 } 8 9 template<typename T>10 void func2(T s) {11 printf("The type is %s\n",typeid(s).name());12 }13 14 int main() {15 int k[5];16 func(k);17 func2(k);18 return 0;19 }20 //The type is int const [5]21 //The type is int *
從輸出結果可以看出,參考型別的函數參數仍然保持了數群組類型,而非參考型別的函數則將實參的類型轉換為指標類型。
4. 模板推演的幾個特殊樣本。
1 #include <stdio.h> 2 #include <typeinfo.h> 3 4 template<typename T> 5 void func(T) { 6 printf("The type is %s\n",typeid(T).name()); 7 } 8 9 void (*pf)(char) = &func; //在本次賦值中完成了類型推演。10 11 int main() {12 pf('A');13 return 0;14 }15 //The type is char
5. 如果在推演的過程中沒有找到精確匹配,然而函數參數是基類類型(或指標),當實參為衍生類別類型(或指標)時,推演仍可成功。
1 #include <stdio.h> 2 #include <typeinfo.h> 3 4 template<typename T> 5 class Base { 6 }; 7 8 template<typename T> 9 class Derive : public Base<T> {10 };11 12 template<typename T>13 void func(Base<T>*) {14 printf("The type of T is %s.\n",typeid(T).name());15 printf("The type of Base<T> is %s.\n",typeid(Base<T>).name());16 }17 18 int main() {19 Derive<int> d;20 func(&d);21 return 0;22 }23 //The type of T is int.24 //The type of Base<T> is class Base<int>.
從上述輸出結果中可以看出,T的類型被成功推演,即便func函數的實參為Derive<int>(Base<int>的衍生類別)。
6. 函數的預設參數不能參與類型推演。
1 #include <stdio.h> 2 3 template<typename T> 4 void func(T x = 100) { 5 6 } 7 8 int main() { 9 func<int>(); //這裡顯示的指定了函數的模參類型,因此可以通過編譯。10 func(100); //通過實參的類型推演出函數模參的類型,也可以通過編譯。11 func(); //不能因為函數參數的預設值是100,就可以利用該規則推演模參類型,不同通過編譯。12 return 0;13 }