一、模板基本概念:
1.1、模板分为函数模板和类模板,当使用这些模板时我们提供特定类型的信息,编译器在编译时会用所提供的类型信息将模板转换为特定的类或函数
1.2、模板参数表示在类或函数定义中用到的类型或值,当使用模板时我们(显示或隐示地)指定模板实参,将其绑定到模板参数上
1.3、模板类型参数可以看做类型说明符,就像内置类型或类类型说明符一样使用。类型参数可以用来指定返回类型或函数的参数类型,以及在函数体内用于变量声明或类型转换例如:
template<typename T> T foo(T* p)
{
T tmp=*p;//tmp的类型是指针p指向的类型
//.......
return tmp;
}
1.4非类型模板参数:除了定义类型参数外,还可以在模板中定义非类型模板参数。一个非类型模板参数表示一个值而非一个类型。通过一个特定的类型名而非关键字class或typename来指定非类型参数。
当一个模板被实例化时,非类型参数被一个用户提供的或编译器推断的值所代替。这些值必须是常量表达式,从而允许编译器在编译时实例化模板。例如:
template<unsigned N,unsigned M>
int compare(const char (&p1)[N],const char (&p1)[M])
{
return strcmp(p1,p2);
}
当我们调用这个版本的compare时,
compare("hi","mom");//编译器推断出N=2,M=3,会实例化出 int compare(const char (&p1)[3],const char (&p1)[4])
一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用。绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期
在模板定义内,模板非类型参数是一个常量值,在需要常量表达式的地方可以使用非类型参数,比如,指定数组的大小。
1.5、编写类型无关的代码:编写泛型代码的两个重要原则
模板中的函数参数是const的引用,保证函数可以用于不能拷贝的类型,同时可以避免拷贝使函数运行的更快
函数体中的条件判断仅使用<比较运算,及模板程序应该尽量减少对实参类型的要求
1.6、模板编译:当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化模板的一个特定版本时,编译器才会生成代码。为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员的函数的定义。因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义,即函数模板和类模板成员函数的定义通常放在头文件中。
二、函数模板
2.1、定义函数模板:一个函数模板就是一个公式,用来生成针对特定类型的函数版本,例如定义一个compare函数模板如下:
template<typename T>
int compare(const T&v1,const T&v2)
{
if(v1<v2) return -1;
if(v1>v2) return 1;
return 0;
}
模板定义以关键字template开始,后跟一个模板参数列表,这是一个逗号分隔的一个或多个模板参数的列表用< >包围起来。上述例子中,T为一个类型参数,T表示的实际类型在编译时根据compare的使用情况来确定。
2.2、实例化函数模板:当我们调用一个含函数模板时,编译器(通常)用函数实参来为我们推断模板实参。编译器用推断出来的模板参数为我们实例化一个特定版本的函数。当编译器实例化一个模板时,它使用实际的模板实参代替对应的模板参数来创建出模板的一个新实例。例如,给定下面的调用:
cout<<compare(0,1)<<endl;//T为int,实例化出int compare(const int&v1,const int&v2)
vector<int> vec1{1,2,3},vec2{4,5,6};
cout<<compare(vec1,vec2)<<endl;//T为vector<int>,实例化出int compare(const vector<int>&v1,const vector<int>&v2)
2.3、inline和constexpr的函数模板:函数模板可以声明为inline和constexpr的,如同非函数模板一样。inline或constexpr说明符放在模板参数列表之后,返回类型之前:
template <typename T> inline T min(const T&,const T&);