声明:
本文大部分解释与理解来自:
https://www.runoob.com/w3cnote/c-templates-detail.html
https://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html
由菜鸟网整理总结,整理文章:
https://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html
作者做的任务只是将知识点简化更供人理解以及加了一些自己的认知。
模版:
模版可以理解成把数据类型做成可以设置的参数化,然后在定义的时候套用,让数据类型可以随意变换。
使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。
函数模版:
格式:
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
//函数体
};
在这段代码中,class 可以替换成 typename ,如下
template <typename 形参名,typename 形参名,......> 返回类型 函数名(参数列表)
{
//函数体
};
在函数模版中,class与typename没有区别。
<>里面的形参叫模版形参,模版形参不能为空。
函数模版定义实例:
template <class T> void swap(T& a, T& b){
};
函数模版不能像类模版一样指定形参类型
编译器会通过你所定义的变量类型,将参数的数据类型自动识别。
例如: (该例子摘自:http://c.biancheng.net/view/315.html)
#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
};
int main()
{
int n = 1, m = 2;
Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
double f = 1.2, g = 2.3;
Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
return 0;
}
类模版:
格式:
template<class 形参名,class 形参名,…> class 类名
{ ... };
在这段代码中,class 可以替换成 typename 。
类模版实例化:
类名<形参的类型列表> 对象名;
非类型形参:
例如 :
template<class T, int a>
int a在模版内部定义是常量类型,所以在传入的时候要注意,如果你传入的是变量,则会报错。传入的一定是常量或者是常量表达式。
非类型模板的形参只能是整型,指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
非类型模板形参的形参和实参间所允许的转换
1、允许从数组到指针,从函数到指针的转换。如:template <int *a> class A{}; int b[1]; A<b> m;即数组到指针的转换
2、const修饰符的转换。如:template<const int *a> class A{}; int b; A<&b> m; 即从int *到const int *的转换。
3、提升转换。如:template<int a> class A{}; const short b=2; A<b> m; 即从short到int 的提升转换
4、整值转换。如:template<unsigned int a> class A{}; A<3> m; 即从int 到unsigned int的转换。
5、常规转换。
默认模板类型形参:
可以为类模板的类型形参提供默认值,但不能为函数模板的类型形参提供默认值。函数模板和类模板都可以为模板的非类型形参提供默认值。
例如:为第二个模板类型形参T2提供int型的默认值。
类模板的类型形参默认值形式为:
template<class T1, class T2=int> class A{};
类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从第一个形参设定了默认值之后的所有模板形参都要设定默认值,比如templateclass A{};就是错误的,因为T1给出了默认值,而T2没有设定。
class 与 typename的区别:
class与typename在参数类型中没有不同。但typename可以在用来标识嵌套依赖类型。
什么是嵌套依赖类型?
就是依赖于模版的形参的类型。
举个例子,如果你在来了模版中要传入的形参是一个类,你在类中使用typedef定义了一个自定义类型,然后要在模版中使用,如果你这样写。
class a{
public:
typedef int BOOL;
};
template<typename T>
class F{
public:
T::BOOL A = 1;//《- 编译器会把T解析成静态变量,所以会报错
void printA(){
cout<<A<<endl;
}
};
int main(){
F<a> f;
f.printA();
}
会报错。因为编译器在class F中会把T看成是静态变量(例如 int char short这些)。所以编译器一定会报错。
为了避免这种情况,可以使用typename来修饰T。
typename的作用就是告诉编译器这是一个类型,不是变量或者是其他东西。
稍加改进之后的代码:
class a{
public:
typedef int BOOL;
};
template<typename T>
class F{
public:
typename T::BOOL A = 1;
void printA(){
cout<<A<<endl;
}
};
int main(){
F<a> f;
f.printA();
}