C++——模板---函数模板---类模板
一、模板
- 模板的引入:
- 模板的精神:类型参数化,即类型也是一种参数。
- template所代表的泛型编程是C++语言中的重要组成部分。C++是一门强类型语言,无法像动态语言(如python)那样,编写一段通用的逻辑,可以把任意类型的变量传进去。泛型编程弥补了这一点摆脱了类型的限制,提高了代码的可重用性。
- 模板是建立通用的与数据类型无关的算法的重要手段,可实现代码重用。
- 函数模板的定义与声明:---函数模板实参推演
- 定义:
- template<模板参数表>返回值类型 函数名(函数形参表){......}//函数体
- 模板参数主要是模板类型参数,尖括号中不能为空。模板类型参数由typename(或class)+标识符构成。表示该标识符代表一种潜在的内置或用户自定义数据类型。
- 声明:
- 函数模板的声明与函数声明不同,函数模板的声明必须含变量名。因为两者的编译过程不一样。函数模板必须先转换成模板函数,再进行编译。模板定义本身不参与编译,而是编译器模板的用户使用模板时提供的类型参数生成代码,再进行编译。这一过程成为模板的实例化。用户提供不同的类型参数就会实例化出不同的代码。
- 定义:
- 类模板的定义与声明:---类模板实例化
- 定义:
-
template<模板参数表> class 类名 { //类定义体 };//注意分号不可少 templete<模板参数表>返回类型 类名<模板参数名表>:: 成员函数名1(形参表) { ......;//成员函数定义体 }
模板参数表有两种:模板类型参数和非模板类型参数。
-
- 定义:
二、函数模板---template关键字用于声明开始进行泛型编程-----typename关键字用于声明泛指类型----函数模板可以自动推导类型进行调用,也可以显示指定具体类型进行调用。
1、普通函数模板:
-
-
1 template<typename T> 2 int compare(const T& left,const T& right) 3 { 4 if(left<rigth) 5 {return -1;} 6 if(right<left) 7 {return 1;}
reurn 0; 8 } 9 10 compare<int>(1,2);//使用模板函数
-
2、类的成员函数模板
不仅普通函数可以定义为模板,类的成员函数也可以定义为模板
-
-
class printer { public: template<typename T> void print(const T& t) { cout<<t<<endl; } } printer p; p.print<const char*>("seu");//打印seu
-
- 实参推断:
为了使用方便,除了直接为函数模板指定类型参数外,还可以让编译器从传递给函数的实参推断参数类型,这一功能被称为模板实参推断。
- 实参推断的使用:
-
1 compare(1,2);//推断T的类型为int 2 compare(1.0,2.0);//推断T的类型为double 3 p.print("absdsaf");//推断T的类型为const char * 4 5 int (*pf)(const int&,const int&)=compare;//推断T的类型为int; 6 //通过把函数模板赋值给一个指定类型的函数指针,让编译器根据这个指针的类型,对模板实参进行判断
-
3、函数模板的重载
函数模板之间,函数模板与普通函数之间可以重载。编译器会根据调用时提供的函数参数,调用能够处理这一类型的最特殊的版本。在特殊性上,一般遵循如下顺序:
- 普通函数
- 特殊函数(限制了T的形式的,指针,引用,容器等)
- 普通模板(对T没有任何限制的)
- 如何判断哪个模板函数更加特殊,原则是:如果模板B的所有实例都可以实例化模板A,而反过来不行,那么B就比A特殊。即B的范围大于A的范围。
-
1 template<typename T> 2 void func(T& t) { //通用模板函数 3 cout << "In generic version template " << t << endl; 4 } 5 6 template<typename T> 7 void func(T* t) { //指针版本 8 cout << "In pointer version template "<< *t << endl; 9 } 10 11 void func(string* s) { //普通函数 12 cout << "In normal function " << *s << endl; 13 } 14 15 int i = 10; 16 func(i); //调用通用版本,其他函数或者无法实例化或者不匹配 17 func(&i); //调用指针版本,通用版本虽然也可以用,但是编译器选择最特殊的版本 18 string s = "abc"; 19 func(&s); //调用普通函数,通用版本和特殊版本虽然也都可以用,但是编译器选择最特化的版本 20 func<>(&s); //调用指针版本,通过<>告诉编译器我们需要用template而不是普通函数
-
4、模板函数的特化
有时候函数模板并不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。函数模板的特化必须把所有的模板参数全部指定。
-
1 template<> 2 void func(int i) { 3 cout << "In special version for int "<< i << endl; 4 } 5 6 int i = 10; 7 func(i); //调用特化版本
三、类模板---
类模板只能显示指定类型参数,无法自动推断参数类型。声明的泛型类型参数可以出现在类模板的任意地方。类模板必须在头文件中实现,不能分开实现在不同的文件中。
类模板适合以相同的逻辑处理不同数据类型的数据,因此非常适合编写数据结构相关的代码。通常用来作为容器(vector)或行为(clonable)的封装。
对于一个类模板printer,只能显示的指定参数类型进行调用
-
printer<int> p(1);正确 printer p(1);错误