多参数类模板:
特化:
上图特化的意义是将原来接收任意两个类型参数的模板特化为只能接收一种类型的两个参数。
这意味着如果定义类时我们给的两个参数是同种类型的,则编译器优先使用右边的这种模板实现。
一个类模板实际分开实现为两个部分,编译器根据具体的参数选择其中的一个模板。
编译器会认为上图中的两个Test模板是同一个,具体实现时,如参数类型不同则选择左边的实现,参数类型相同时选择右边的实现。
完全特化就是我们根本就不需要泛指参数了,直接指定原先的泛指类型全部为int,完全特化意味着特化之后的模板里面已经没有了泛指类型。
当我们使用模板时,如果指定了参数都为int,则编译器选择右边的实现。
示例程序:
编译器并不认为第20行是一个新的模板,只是认为它是第7行那个模板的一个特殊实现。
使用double*和int*来生成类:
1 Test<int*, double*> t4; 2 int a = 1; 3 double b = 0.1; 4 5 t4.add(&a, &b);
这次匹配到了第7行的这个模板实现,但是第二次编译时,编译器提示我们两个不同的指针类型无法相加。
因此,我们继续添加指针参数的特化。
继续完善:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Test 9 { 10 public: 11 void add(T1 a, T2 b) 12 { 13 cout << "void add(T1 a, T2 b)" << endl; 14 cout << a + b << endl; 15 } 16 }; 17 18 template 19 < typename T1, typename T2 > 20 class Test < T1*, T2* > // 关于指针的特化实现 21 { 22 public: 23 void add(T1* a, T2* b) 24 { 25 cout << "void add(T1* a, T2* b)" << endl; 26 cout << *a + *b << endl; 27 } 28 }; 29 30 template 31 < typename T > 32 class Test < T, T > // 当 Test 类模板的两个类型参数完全相同时,使用这个实现 33 { 34 public: 35 void add(T a, T b) 36 { 37 cout << "void add(T a, T b)" << endl; 38 cout << a + b << endl; 39 } 40 void print() 41 { 42 cout << "class Test < T, T >" << endl; 43 } 44 }; 45 46 template 47 < > 48 class Test < void*, void* > // 当 T1 == void* 并且 T2 == void* 时 49 { 50 public: 51 void add(void* a, void* b) 52 { 53 cout << "void add(void* a, void* b)" << endl; 54 cout << "Error to add void* param..." << endl; 55 } 56 }; 57 58 int main() 59 { 60 Test<int, float> t1; 61 Test<long, long> t2; 62 Test<void*, void*> t3; 63 64 t1.add(1, 2.5); 65 66 t2.add(5, 5); 67 t2.print(); 68 69 t3.add(NULL, NULL); 70 71 Test<int*, double*> t4; 72 int a = 1; 73 double b = 0.1; 74 75 t4.add(&a, &b); 76 77 return 0; 78 }
以上的四个类模板是一个模板,只不过有几种不同的特化方式。
结果如下:
多参数类模板:
问题:
特化的深度分析:
示例:
我们将void* 指针参数的特化注释掉,重新定义Test_Void类,第75行也改成Test_Void类名字,这样也能正常工作。
但是,在我们编程时要时刻想着到底是使用类模板呢还是Test_Void类呢?这无疑增加了负担,所以能用类模板特化的就用特化,不能特化的再重新定义类。
函数模板示例:
上图中我们得到了正确的结果,但是我们知道两个浮点数比较不能直接用45行的比较方法,因此,我们需要特化。
改进如下:
从打印结果看,double类型确实调用到了第52行的特化版本。
再添加一个直接重载的版本:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Test 9 { 10 public: 11 void add(T1 a, T2 b) 12 { 13 cout << "void add(T1 a, T2 b)" << endl; 14 cout << a + b << endl; 15 } 16 }; 17 18 /* 19 template 20 < > 21 class Test < void*, void* > // 当 T1 == void* 并且 T2 == void* 时 22 { 23 public: 24 void add(void* a, void* b) 25 { 26 cout << "void add(void* a, void* b)" << endl; 27 cout << "Error to add void* param..." << endl; 28 } 29 }; 30 */ 31 32 class Test_Void 33 { 34 public: 35 void add(void* a, void* b) 36 { 37 cout << "void add(void* a, void* b)" << endl; 38 cout << "Error to add void* param..." << endl; 39 } 40 }; 41 42 template 43 < typename T > 44 bool Equal(T a, T b) 45 { 46 cout << "bool Equal(T a, T b)" << endl; 47 48 return a == b; 49 } 50 51 template 52 < > 53 bool Equal<double>(double a, double b) 54 { 55 const double delta = 0.00000000000001; 56 double r = a - b; 57 58 cout << "bool Equal<double>(double a, double b)" << endl; 59 60 return (-delta < r) && (r < delta); 61 } 62 63 bool Equal(double a, double b) 64 { 65 const double delta = 0.00000000000001; 66 double r = a - b; 67 68 cout << "bool Equal(double a, double b)" << endl; 69 70 return (-delta < r) && (r < delta); 71 } 72 73 int main() 74 { 75 cout << Equal( 1, 1 ) << endl; 76 cout << Equal<>( 0.001, 0.001 ) << endl; 77 78 return 0; 79 }
运行结果如下:
当第76行为cout << Equal(0.001, 0.001) << endl 时,结果如下:
调用的是我们的重载的函数版本。
当第76行为cout << Equal<>(0.001, 0.001) << endl 时,调用的是函数模板特化版本,如下:
工程中的建议:
小结: