C++之模板
最近因为工作原因,需要掌握C++中模板的用法,故分三部分作简单学习笔记如下:
1.函数模板
模板可以让我们生成通用参数类型的函数,这个通用的函数能够接受任意类型参数,同样可以返回任意类型的值,这样就避免了对所有类型的函数进行重载。
譬如说:我们可能在业务开发中会用到类似如下函数
1 #include<iostream> 2 using namespace std 3 4 int Plus(int a,int b) 5 { 6 int Sum; 7 Sum = a + b; 8 return Sum; 9 } 10 11 double Plus(double a,double b) 12 { 13 double Sum; 14 Sum = a + b; 15 return Sum; 16 }
如上所示,当我们在计算两个数之和时,常常会遇到整型和浮点型两种不同的参数,这样,我们就不得不使用函数的重载来实现。如果假设,我们某一个接口函数有很多不同类型的入参,都有相同的实现,那么我们将会设计很多重载来实现,这样将会带来很多重复性的工作。而我们程序员就是要消灭这种重复性的工作。所以,C++中引入了函数模板的概念。
模板,是将一个事物的结构规律予以固定化,规律化的成果。既然在程序中,大家的实现都一致,只是入参和返参不同,那么我们就定义一个通用的入参和返参,接口内部同样一种实现。如此,就产生了函数模板。接下来,我们在代码中认识一下它。
1 template <class Name> Function 2 template <typename Name> Function
如上所示,这就是一个函数模板的定义,首先我们要有关键字template,后面紧跟一个尖括号,里面关键字可以使用class或typename,后面的Name是由我们自己定义的名称,最后面Function就是我们的函数实现了。就拿前文中提到的计算两个数之和的函数来举例,我们来看下如果使用函数模板是如何实现的:
1 #include<iostream> 2 using namespace std; 3 template <class G_Type> 4 G_Type Plus(G_Type a, G_Type b) 5 { 6 G_Type Sum; 7 Sum = a + b; 8 return Sum; 9 } 10 int main() 11 { 12 int a = 10, b = 20, Sum1; 13 double c = 3.14, d = 6.28, Sum2; 14 Sum1 = Plus(10,20); 15 Sum2 = Plus(3.14,6.28); 16 cout << Sum1 << endl; 17 cout << Sum2 << endl; 18 return 0; 19 }
运行后结果:
以上,我们使用了一个函数模板的方式省去了写多个重载函数的时间。
补充:
上述我们的模板只有一个通用类型参数即G_Type,所以,当我们定义好之后,只能使用同样的类型参数去调用模板函数,譬如都是整型,或都是浮点型,那如果说我们想用不同类型参数去调用这个函数呢?一起来看下面的例子
1 Sum1 = Plus(10,20); //可实现 2 Sum2 = Plus(3.14,6.28); // 可实现 3 Sum3 = Plus(3.14,10); //不可实现,这种情况在上述模板就无法实现,为了应对这种多种类型的参数,我们需要对函数模板进行小的修改。 4 template <class G_Type,class U_Type> 5 G_Type Plus(G_Type a, U_Type b) 6 { 7 G_Type Sum; 8 Sum = a + b; 9 return Sum; 10 }
如上,我们新增一个通用类型名为U_Type,这样我们就可以将函数模板适用于多参数。
2.类模板
上述我们学习了函数模板,我们同样也可以实现类模板。类模板的实现,可以使类有通用类型的成员,不必在定义类的时候定义成员的类型,即类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。
首先,我们还是在程序中认识一下类模板:
//类模板实现 template<class Name> class Student { public: Name m_Member;//成员函数定义 public: Name m_Add(Name a, Name b);//成员函数声明 };
如上所示,这就是一个类模板的定义,首先我们还是要有关键字template,后面紧跟一个尖括号,里面关键字可以使用class或typename,后面的Name是由我们自己定义的名称,下面就是我们的Student类的实现了。我们仍旧使用前文中提到的计算两个数之和的函数来举例,这一次我们来看下如果使用类模板又是如何实现的:
1 #include <stdio.h> 2 3 //类模板实现 4 template<class Name> 5 class Student 6 { 7 public: 8 Name m_Member;//成员函数定义 9 public: 10 Name m_Add(Name a, Name b);//成员函数声明 11 }; 12 13 //成员函数的外部实现 14 template <class Name> 15 Name Student<Name>::m_Add(Name a, Name b) 16 { 17 return a + b; 18 } 19 20 //主函数 21 int main() 22 { 23 Student<int> stu; 24 printf("stu.m_Add(1,2) = %d \n",stu.m_Add(1,2)); 25 return 0; 26 }
运行后结果:
以上,我们就认识了类模板,并且学会了简单的应用。
补充:
类模板和函数模板一样,同样可以定义多种类型不同的参数,下面我们还是用程序代码来说明
1 //类模板实现 2 template<class Name1,class Name2> 3 class Student 4 { 5 public: 6 Name1 m_Member1; 7 Name2 m_Member2;//成员函数定义 8 public: 9 void m_Add(Name1 a, Name2 b);//成员函数声明 10 }; 11 12 //成员函数的外部实现 13 template <class Name1,class Name2> 14 void Student<Name1, Name2>::m_Add(Name1 a, Name2 b) 15 { 16 return a + b; 17 }
3.模板的特化
a.函数模板特化
函数模板在某种特定类型下的特定实现称为函数模板的特化。
1 template <typename T> 2 T add(T a, T b) 3 { 4 printf("T add(T a, T b)\n"); 5 return a + b; 6 } 7 //上述函数不支持int*类型,所以要对传入指针类型参数进行特化。 8 template <> 9 int* add<int*>(int* pa, int* pb) 10 { 11 printf("T add(const int* pa, const int* pb)\n"); 12 *pa += *pb; 13 return pa; 14 } 15 16 int main(void) 17 { 18 int a = 8, b = 6; 19 add<>(&a, &b); //<>,编译器会自动推导数据类型为int* 20 21 return 0; 22 }
b.类模板的特化
类模板特化有部分特化与完全特化
首先定义一个类模板和类的成员外部实现
1 template <typename T1, typename T2> 2 class TestCls 3 { 4 public: 5 void add(T1 a, T2 b); 6 }; 7 8 template <typename T1, typename T2> 9 void TestCls <T1, T2>::add(T1 a, T2 b) 10 { 11 printf("estCls <T1, T2>::add(T1 a, T2 b) = %d\n", a + b); 12 } 13 14 //部分特化 15 template <typename T> 16 class TestCls <T, T> 17 { 18 public: 19 void add(T a, T b); 20 }; 21 22 template <typename T> 23 void TestCls <T, T>::add(T a, T b) 24 { 25 printf("TestCls <T, T>::add(T a, T b) = %d\n", a + b); 26 } 27 28 int main(void) 29 { 30 TestCls<int, int> t; //编译器会选择特化后的类。 31 //注意<>内不能为空,编译器对类模板不能进行类型推导。 32 t.add(5, 5); 33 return 0; 34 }
以上称为部分特化,我们原先的设定是T1、T2两种类型,二者既可以是同一种类型,也可以是不同一种,特化后T1、T2只能是相同的一种类型T。当我们定义的对象的T1、T2类型是一样的时候,编译器会选择特化后的类模板
1 //完全特化,完全特化后"<>"内为空 2 template <> 3 class TestCls<int, char> //直接指定了T1、T2的类型 4 { 5 public: 6 void add(int a, char b) 7 { 8 printf("TestCls<int, char>::add(int a, char b)\n"); 9 } 10 }; 11 12 int main(void) 13 { 14 TestCls<int, char> t; 15 t.add(5, 5); 16 return 0; 17 }
以上,即本次学习模板后的学习笔记,后续深入学习再持续补充,上述内容如有不足之处,望各位读者批评指正。
leo zhai
Nov,23.2018
leonardozhai@outlook.com
模板特化部分参阅以下两篇文章:
c++的函数模板-作者:bright261
C++ 高级篇(一)—— 模板(Templates)-作者:zqixiao_09