模板(Template)是允许函数或者类通过泛型(generic types)的形式表现或运行的特性。
模板可以使函数或者类只写一份代码而对应不同的类型。
一种独立于特定类型的编码方式
模板分为函数模板与类模板两类。
template <模板形参表> 函数返回类型 函数(形参表);
template <模板形参表>
函数返回类型 函数(形参表){
函数体;
};
例如:
template <typename T>
T Max(T a,T b){
return a>b?a:b;
}
函数(实参表)
产生模板特定类型的函数或者类的过程称为实例化
调用函数模板与调用函数完全一致。
Max()
,Min()
Ston()
template <模板形参表> class 类名;
template <模板形参表>
class 类名 {
}
类名<模板实参表> 对象;
,
分割。模板参数,模板参数,...
class 类型形参
或者typename 类型形参
类模板的声明与实现通常都写在头文件中,是不能够分开的。
Complex
Triangle
模板参数推导/推演(deduction):由模板实参类型确定模板形参的过程。
实例化有两类:
类模板参数允许自动类型转换(隐式转换);函数模板参数不允许自动类型转换(隐式转换)
在模板参数列表中,class
和typename
完全一样。但是在语义上,class
表示类,typename
代表所有类型(类以及基本类型)。
请尽量使用typename
函数模板实参类型不一致问题
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
模板实例化时,
Max(2,2.4)
参数推导会出现模板实参类型int
与double
不一致的错误。
解决方法:
template <typename T , typename U> inline const T& Max(const T& a, const U& b){
return a>b?a:b;
}
注意:这种解决方法还有一个问题,就是返回值只能强制设置为T
或者U
,不能自动推导。C++11的后置推导解决这个问题。
template <typename T, typename U>
inline auto Max(const T& a, const U& b)->decltype(a>b?a:b)
{
return a>b?a:b;
}
Max<int>(2,2.4)
或者
Max<double>(2,2.4)
Max(2,static_cast<int>(2.4))
或者
Max(static_cast<double>(2),2.4)
模板参数推导不允许类型自动转换,模板参数必须严格匹配。
函数模板实例显示指定模板实参可以显示指定模板实参,也可以不指定(类型自动推导),类模板实例化必须
模板特化分类
特点:函数模板,却只有全特化,不能偏特化。
步骤:与类的全特化相同
示例:
template<typename T>
void Func(const T& n){}
// 特化
template<>
void Func(const int& n){}
特点:类模板特化,每个成员函数必须重新定义。
类模板特化分为两种
步骤:
template<>
<>
中显示指定类型。// 模板
template<class T>
class Test{};
// 全特化
template<>
class Test<int*>{};
偏特化就是部分特化,分为两种情况
步骤:
示例:
template<typename T1,typename T2>
class Test{};
template<typename T>
class Test<T,T>{};
template<typename T>
class Test<T,int>{};
template<typename T1,typename T2>
class Test<T1*,T2*>{};
实例:三元组模版Triple
类模板特化,相当于函数模板的重载
全特化和偏特化的编码区别:
全特化的模板参数列表为空template<>
,偏特化的模板参数列表不为空。
#include
using namespace std;
template <typename T,typename S,typename U>
class Triple{ //元组:里面的元素类型可以不同 / 数组:里面的元素类型必须相同
T t;
S s;
U u;
public:
Triple(T t,S s,U u):t(t),s(s),u(u){
cout << "Template"
}
T& first(){return t;}
S& second(){return s;}
U& third(){return u;}
};
///偏特化(指定某几个模板参数为具体类型)
template<typename S,typename U>
class Triple<string,S,U>{
string t;
S s;
U u;
public:
Triple(string t,S s,U u):t(t),s(s),u(u){
cout << "Template" << endl;
}
string& first(){return t;}
S& second(){return s;}
U& third(){return u;}
};
template <typename T,typename S,typename U>
class Triple<const T&,const S&,const U&>{ //元组:里面的元素类型可以不同 / 数组:里面的元素类型必须相同
const T& t;
const S& s;
const U& u;
public:
Triple(const T& t,const S& s,const U& u):t(t),s(s),u(u){
cout << "Template" << endl;
}
const T& first(){return t;}
const S& second(){return s;}
const U& third(){return u;}
};
///全特化
template<> //有特化必须有对应的基础模板
class Triple<string,bool,int>{
string t;
bool s;
int u;
public:
Triple(string t,bool s,int u):t(t),s(s),u(u){
cout << "Template" << endl;
}
string& first(){return t;}
bool& second(){return s;}
int& third(){return u;}
};
class Test{};
int main()
{
Triple<string,bool,int> st("张三",true,24); //先找特化,没有匹配到再找基础模板
const Test t;
Triple<const Test&,const Test&,const Test&> st2(t,t,t);
cout << st.first() << st.second() << st.third() << endl;
}
模板原理
非类型模版参数
非类型模板的实参只能是整型常量、枚举值或者指向外部链接对象的指针。
不能使用浮点型、类对象、内部链接对象的指针。
技巧
函数模板参数尽量使用引用类型const &
例如:
template <typename T> inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
类模板
成员函数:只有调用时才会被实例化。
静态成员:每次类模板实例化,都会被实例化。
类实例化成对象,类模板实例化成类。
实例
#include
#include
using namespace std;
// 引用类型模板
template <typename T>
bool Equal(const T& a,const T& b){
return a == b;
}
// 特化成浮点型
template<>
bool Equal(const double& a,const double& b){
return abs(a-b) < 1e-6;
}
// -------------------------------------------------
// 指针类型模板(函数模板重载)
template<typename T>
bool Equal(const T* a,const T* b){
return *a==*b;
}
// 特化成char*
template<>
bool Equal(const char* a,const char* b){
return strcmp(a,b)==0;
}
int main(){
cout << Equal(1,1) << endl;
cout << Equal(1.2,1.2) << endl;
cout << Equal(string("abc"),string("abc")) << endl;
cout << Equal(1,2) << endl;
cout << Equal(1.2,1.21) << endl;
cout << Equal(string("abcd"),string("abc")) << endl;
cout << Equal(1.2,(10.2-9)) << endl;
int arr[] = {1,2,3,1};
cout << Equal(arr,arr+3) << endl; // bool Equal(int*,int*)
cout << Equal("abc","abcd") << endl;
}