Step By Step(C++模板推演)
普通重载函数可以通过函数参数进行推演,并由编译器选定最适合的重载函数作为候选函数。与此类似,模板函数可以通过函数参数的类型推演出该函数模参的实际类型。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的值是100 44 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 void 53 //The type of T2 is class Test 54 //The type of T3 is double 55 //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 }