一、模板参数
模板参数分为类型形参和非类型形参。
1.类型形参
出现在模板参数列表中,跟在class或typename之后的参数类型名称。如下代码中跟在class后的T就是类型形参:
1. template<class T> 2. void Swap(T& a, T& b) 3. { 4. T temp = a; 5. a = b; 6. b = temp; 7. }
2.非类型形参
用一个常量作为类或函数模板的一个参数,在类或函数模板中把这个参数当成常量来用,如下代码中的N就是非类型形参,它是模板的另外一个参数:
1. //非类型模板参数 2. template<class T,size_t N>//N被当作常量,不允许被修改 3. class Car 4. { 5. private: 6. T _number[N];//汽车数量 7. }; 8. 9. int main() 10. { 11. Car<int, 3> car1;//N=3 12. Car<int, 20> car2;//N=20 13. 14. return 0; 15. }
可以使用缺省的模板参数
1. //非类型模板参数 2. template<class T = int, size_t N = 100>//缺省模板参数 3. class Car 4. { 5. private: 6. T _number[N];//汽车数量 7. }; 8. 9. int main() 10. { 11. Car<int, 3> car1;//N=3 12. Car<int, 20> car2;//N=20 13. Car<> car3;//使用了缺省的模板参数 14. return 0; 15. }
注意:
(1)非类型模板参数必须是整形,不允许其他类型。
template<double N>//N是非类型模板参数,编译报错,类型非法,不允许double型
(2)非类型的模板参数必须在编译期就能确认结果。
1. //非类型模板参数 2. template<class T, size_t N>//模板参数没有给默认值 3. class Car 4. { 5. private: 6. T _number[N];//汽车数量 7. }; 8. 9. int main() 10. { 11. Car<int> car4;//编译报错,没有指定非类型模板参数的值 12. return 0; 13. }
二、模板的特化
有时候,编译默认函数模板或者类模板不能正确处理需要的逻辑,需要针对指定类型进行特殊化处理,就要做模板的特化。
比如如下代码:
1. template<class T> 2. bool IsEqual(const T& T1, const T& T2) 3. { 4. return T1 == T2; 5. } 6. void Test() 7. { 8. char c1[] = "C++"; 9. char c2[] = "C++"; 10. cout << IsEqual(c1, c2) << endl; 11. 12. const char* p1 = "C++"; 13. const char* p2 = "C++"; 14. cout << IsEqual(p1, p2) << endl; 15. 16. } 17. 18. int main() 19. { 20. Test(); 21. return 0; 22. }
发现打印结果一个为0,一个为1:
c1和c2是数组,在栈上,c1是第一个数组的地址,c2是第二个数组的地址,c1和c2分别把常量区的字符串"C++"拷贝过去了,c1是第一个数组的地址,c2是第二个数组的地址,所以它俩不相等。
p1和p2是指针,都指向常量字符串"C++","C++"位于常量区,p1和p2里面存的都是地址,都存的是"C++"的地址,所以p1和p2相等。
虽然p1和p2的比较结果是1,是正确的。但是比较两个字符串是否相等,应该比较的是这两个字符串的ASCII码,而不是直接用==来比较。这就需要模板的特化。
模板的特化是在原有模板的基础上,针对特殊类型,所进行特殊化的实现方式,特化的本质是显式指定实例化的模板。模板特化分为函数模板特化和类模板特化。
1.函数模板特化
函数模板特化的步骤:
(1)必须有基础模板函数
(2)template后面跟一对空的尖括号<>
(3)函数名后跟一对尖括号,用来指定特化的类型
(4)函数形参表:必须要和模板函数的基础参数类型完全相同,否则编译器报错。
如上面的字符串比较不能用==来判断字符串是否相等,需要进行模板特化,用strcmp来比较:
1. #include<iostream> 2. using namespace std; 3. 4. //函数模板 5. template<class T> 6. bool IsEqual(const T& T1, const T& T2) 7. { 8. return T1 == T2; 9. } 10. 11. //标准的模板特化 12. template<> 13. bool IsEqual<const char*>(const char* const& left, const char* const& right) 14. { 15. if (strcmp(left, right) == 0) 16. { 17. return true; 18. } 19. return false; 20. } 21. 22. void Test() 23. { 24. const char* p1 = "C++"; 25. const char* p2 = "C++"; 26. cout << IsEqual(p1, p2) << endl; 27. } 28. 29. int main() 30. { 31. Test(); 32. return 0; 33. } 34.
以上第 11-19行是标准的模板特化的写法,为了实现简单,通常都将函数直接给出:
1. //简写的模板板特化 2. bool IsEqual(char* left, char* right) 3. { 4. if (strcmp(left, right) == 0) 5. { 6. return true; 7. } 8. return false; 9. }
2.类模板特化
类模板在没有写类特化之前,调的都是原模板的,如下代码,定义了类模板:
1. #include<iostream> 2. using namespace std; 3. 4. //类模板 5. template<class T1, class T2> 6. class Data 7. { 8. public: 9. Data() 10. { 11. cout << "Data<T1,T2>" << endl; 12. } 13. }; 14. 15. void Test_Class() 16. { 17. Data<int, int> d1; 18. Data<int, char> d2; 19. } 20. 21. int main() 22. { 23. return 0; 24. }
类模板分为全特化和偏特化,但是类模板只有一种写法,没有简写形式
(1)全特化
全特化是把模板参数列表中的所有参数都确定化。
比如想把T1指定成int,T2指定成char
1. #include<iostream> 2. using namespace std; 3. 4. //类模板 5. template<class T1, class T2> 6. class Data 7. { 8. public: 9. Data() 10. { 11. cout << "Data<T1,T2>" << endl; 12. } 13. }; 14. 15. //类模板的全特化,将T1指定成int,T2指定成char 16. template<> 17. class Data<int, char> 18. { 19. public: 20. Data() 21. { 22. cout << "Data<int,char>" << endl; 23. } 24. private: 25. int _d1; 26. char _d2; 27. }; 28. 29. void Test_Class() 30. { 31. Data<int, int> d1; 32. Data<int, double> d2; 33. Data<int, char> d3; 34. } 35. 36. int main() 37. { 38. return 0; 39. }
那么Data<int, char> d3;就会去调用全特化的模板。Data<int, int> d1;和Data<int, double> d2;就会调用原有的类模板: