模板是泛型编程的基础,所谓模板就是编写与类型无关的代码,模板是一种复用的手段,模板分为模板函数和模板类。
比如说,我们要实现一个判断两个数是否相等的函数,传递的两个参数不同,如果没有模板,就需要各自实现一份代码,而有了模板之后,只需要实现一份代码即可,编译器就会根据传递的参数自动推演出模板形参的类型。有时候当我们传递的两个函数的参数类型不相同时,编译器就会报错,这时,我们就需要对模板参数显示实例化,实例化的类型不同,所得到的结果也就不同。
1、模板函数
模板函数的定义格式:
template <class 形参名1,class 形参名2...,class 形参名3>
//这里class也可以换为typename
返回值类型 函数名()
{...}
template <class T>
bool Isequal(T &a, T &b)
{
return a == b;
}
int main()
{
string s1("hello");
string s2 = s1;
cout<<Isequal(1, 2)<<endl;
cout << Isequal(s1, s2) << endl;
cout << Isequal<int>(1, 1.2) << endl; //模板参数的显示实例化 结果为1
cout << Isequal<double>(1, 1.2) << endl; //模板参数的显示实例化,结果为0
system("pause");
return 0;
}
2.模板类
什么是类模板?
一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员、默写成员函数的参数、某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。
如果一个类中数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表着一类类。类模板中的成员函数全部都是模板函数(该说法正确)。
什么是模板类
模板类是类模板实例化后的一个产物。可以从类模板派生出新的类,既可以派生类模板,也可以派生非模板类。
模板类的定义格式
template <class 形参名1,class 形参名2...,class 形参名n>
class 类名
{...}
下面以容器适配器为例进行分析
template <typename T>
class SeqList
{
private:
int _size;
int _capacity;
T* _data;
};
template <class T ,class Container=SeqList<int>>//Container就是一个模板形参,模板实参传什么类型,Container就是什么类型
class Stack
{
public:
void push(const T &x);
void pop();
const T& top();
bool empty();
protected:
Container _con;
};
int main()
{
Stack<int> s1;
Stack<int, SeqList< int>> s2;
system("pause");
return 0;
}
3.模板的模板类
template <typename T>
class SeqList
{
private:
int _size;
int _capacity;
T* _data;
};
template <class T ,template <class> class Container=SeqList>//缺省参数,这里template <class>表示Container是一个模板类型的模板形参
class Stack
{
public:
void push(const T &x);
void pop();
const T& top();
bool empty();
protected:
Container<T> _con;
};
4.模板类的特化
C++中的模板特化不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。模板特化有时也称之为模板的具体化。模板的全特化和偏特化都是在已经定义的模板类之上,不能单独存在。
特化整体上分为全特化和偏特化
全特化:模板中的模板参数全部被指定为特定的类型,全特化也就是定义了一个全新的类型。完全产生确定的东西,不需要在编译期间搜寻合适的特化实现。
偏特化:模板中的模板参数没有被全部确定,需要在编译的时候进行确定
模板函数只可以进行全特化,而模板类既可以进行全特化,也可以进行偏特化
根据特化的对象可分为
(1)函数模板特化:当函数中需要对某些类型进行特化处理时,称为函数模板的特化
类模板的特化实例:
/// 模版特化
template <class T>
int compare(const T left, const T right)
{
std::cout <<"in template<class T>..." <<std::endl;
return (left - right);
}
// 这个是一个特化的函数模版
template < >
int compare<const char*>(const char* left, const char* right)
{
std::cout <<"in special template< >..." <<std::endl;
return strcmp(left, right);
}
// 特化的函数模版, 两个特化的模版本质相同, 因此编译器会报错
// error: redefinition of 'int compare(T, T) [with T = const char*]'|
//template < >
//int compare(const char* left, const char* right)
//{
// std::cout <<"in special template< >..." <<std::endl;
//
// return strcmp(left, right);
//}
// 这个其实本质是函数重载
int compare(char* left, char* right)
{
std::cout <<"in overload function..." <<std::endl;
return strcmp(left, right);
}
int main( )
{
compare(1, 4);
const char *left = "hello";
const char *right = "world";
compare(left, right);
return 0;
}
当函数调用调用有特化函数时,会优先调用特化的匹配函数,而不是通过函数模板进行实例化。
(2)类模板的特化:当类中需要对某些类型进行特化处理时,称为类模板的特化
模板参数的类模板特化有如下三种情况:
·特化为绝对类型
·特化为引用、指针类型
·特化为另外一个模板
下面分别就这三种情况进行说明:
特化为绝对类型:
#include <iostream>
#include <cstring>
#include <cmath>
// general version
template<class T>
class Compare
{
public:
static bool IsEqual(const T& lh, const T& rh)
{
std::cout <<"in the general class..." <<std::endl;
return lh == rh;
}
};
// specialize for float
template<>
class Compare<float>
{
public:
static bool IsEqual(const float& lh, const float& rh)
{
std::cout <<"in the float special class..." <<std::endl;
return std::abs(lh - rh) < 10e-3;
}
};
// specialize for double
template<>
class Compare<double>
{
public:
static bool IsEqual(const double& lh, const double& rh)
{
std::cout <<"in the double special class..." <<std::endl;
return std::abs(lh - rh) < 10e-6;
}
};
int main(void)
{
Compare<int> comp1;
std::cout <<comp1.IsEqual(3, 4) <<std::endl;
std::cout <<comp1.IsEqual(3, 3) <<std::endl;
Compare<float> comp2;
std::cout <<comp2.IsEqual(3.14, 4.14) <<std::endl;
std::cout <<comp2.IsEqual(3, 3) <<std::endl;
Compare<double> comp3;
std::cout <<comp3.IsEqual(3.14159, 4.14159) <<std::endl;
std::cout <<comp3.IsEqual(3.14159, 3.14159) <<std::endl;
return 0;
}
特化为引用指针类型:
template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
// specialize for _Tp*
template <class _Tp>
struct iterator_traits<_Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
// specialize for const _Tp*
template <class _Tp>
struct iterator_traits<const _Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
特化为另外一个模板:
// specialize for vector<T>
template<class T>
class Compare<vector<T> >
{
public:
static bool IsEqual(const vector<T>& lh, const vector<T>& rh)
{
if(lh.size() != rh.size()) return false;
else
{
for(int i = 0; i < lh.size(); ++i)
{
if(lh[i] != rh[i]) return false;
}
}
return true;
}
};
我们把IsEqual()的参数类型定义为vector类型,这种类型叫做半特化类型,我们可以把这种半特化类型自定义为我们的模板类类型
// specialize for any template class type
template <class T1>
struct SpecializedType
{
T1 x1;
T1 x2;
};
template <class T>
class Compare<SpecializedType<T> >
{
public:
static bool IsEqual(const SpecializedType<T>& lh, const SpecializedType<T>& rh)
{
return Compare<T>::IsEqual(lh.x1 + lh.x2, rh.x1 + rh.x2);
}
};
5.模板不支持分离编译
模板是不支持分离编译的,如下是之前总结的模板为什么不支持分离编译
https://blog.youkuaiyun.com/wyn126/article/details/76733943
6.模板总结:
优点:模板复用了代码,节省资源,C++标准库因此而产生
增强了代码的灵活性
缺点:模板让代码变得凌乱复杂,不易维护,增加了编译时间
出现模板编译错误时,错误信息非常凌乱,不易维护
每种类型的参数都会产生额外的代码,导致文件膨胀,代码量过长,