模板是泛型编程基础,即编写类型无关的代码,可以减少重复代码的出现。
函数模板
函数重载是指一系列同名函数,它们的函数特征标不同。或参数类型不同,或参数个数不同,或这二者都不同。但如果是要实现功能类似的函数,只是具体类型不同,如:
int add(int, int);
float add(float, float);
double add(double, double);
则这三个函数的函数体几乎一模一样。此时,就可以定义一个通用的模板:
template<typename T,char op = '+'>
T Operator(T a,T b){
if(op=='+')return a+b;
else if(op=='-')return a-b;
//...
}
//main()
std::cout<<Operator(2,3)<<std::endl;//5
std::cout<<Operator<int,'-'>(4,2)<<std::endl;//2
上述模板定义中,T是类型参数,可以表示任意类型;op是非类型参数,提供了默认值+,但非类型参数一般不能是浮点型或者类类型。在main()中,可以看到编译器能够根据实参推断模板参数,也可以显式指定。为了不出错,建议全部显式指定。
类模板
类是C++中的核心概念,也可以编写通用类模板。常用的vector、stack等都是基于模板实现的。
//.h
template<typename T>
class ownVector{
public:
typedef T* iter;//or using iter = T*;
ownVector()=default;
ownVector&operator=(const ownVector&);
template<typename T1>
ownVector(T1 v);
iter begin();
//iter end();
};
template<typename T>
ownVector<T>&ownVector<T>::operator=(const ownVector<T>&){}
template<typename T>
template<typename T1>
ownVector<T>::ownVector(T1 v){}
template<typename T>
typename ownVector<T>::iter ownVector<T>::begin(){}
类外使用模板名时,需要标明模板参数。typename关键字既可以声明类型参数,也可以用来通知编译器某个名字代表的是类型:typename ownVector<T>::iter。
类模板的成员函数也可以是函数模板。成员函数模板和类模板拥有各自独立的模板参数。如果在类外书写成员函数定义,则需要先写类模板的模板参数列表,再写成员函数模板的模板参数列表。
其它
模板在使用时才会实例化。模板定义并不会生成相关代码,只有在实例化时才生成特定版本的代码。由于实例化需要找到相关定义,因此,模板的编写一般都放在.h文件中。
由于每次调用都会生成实例化代码,如果多次使用就会生成多个相同模板实例,增加了很多编译时间。可以通过“显式实例化”来避免这种情况:template template-name<template-parameters>;,该语句表明编译器会实例化出一份代码供本文件使用。如果在其它文件中也想使用该定义,则可以进行“实例化声明”:extern template template-name<template-parameters>。