一、什么是模板
在C++中,模板(template)是一种通用的编程工具,允许程序员编写通用代码以处理多种数据类型或数据结构,而不需要为每种特定类型编写重复的代码,通过模板,可以实现代码的复用和泛化,提高代码的灵活性和可维护性———简而言之就是可以写一份通用的模板,便于阅读和管理
二、模板的声明:
template<class T>
//or
template<typename T>
模板的声明方式为以上两种,以下的代码主要展示第一种声明方式
三、模板的分类
模板可以分为下面几类
1、函数模板
使用函数模板可以编写处通用的函数,从而可以使用相同的函数逻辑以处理不同类型的参数。
函数模板的使用情形:
template<class T>
T Add(const T& x,const T& y)
{
return x+y;
}
int main()
{
int a=1,b=2;
int c=Add(a,b);
}
函数模板会根据传入参数的类型自动推导出T是什么类型,就比如上面代码中,我们传入了int 型参数a和b,编译器自动推导出在这里T类型就是int 型,并在编译过程中自动生成一份下将T替换为int 的代码,如下所示
int Add(const int& x,const int& y)
{
return x+y;
}
这样一来,在调用Add函数的时候,其本质上是调用了上面的经过实例化的模板函数,这样做的好处就是,程序员就可以不用对每一种数据类型都去实现对应的函数,大大提高了效率,而且只实现这么一份通用的模板函数可以使其更易于维护。
2、类模板
类模板允许定义通用的类,从而可以处理不同类型的数据,通过类模板,可以实现通用的数据结构或算法,如vector、list等可以存储不同数据类型的容器。
template<class T>
class my_arr
{
public:
\\....
private:
T* val[10];
};
类模板是在类的前面加上template<class...>,与函数模板不同的是,类模板在使用时要指定T为某个具体的数据类型,以供编译器进行实例化,而函数模板则是传入参数从而推导出T的类型。
具体使用方式如下
my_container<int> arr;
这样便可以定义一个int类型的数组。
当然,无论是类模板还是函数模板都可以使用非类型参数,就拿上面的my_arr类来举例
template<class T,int N>
class my_arr
{
public:
\\....
private:
T* val[N];
};
my_container<int,10> arr1;
my_container<int,100> arr2;
这样,便可以通过传入不同的N值来控制对象数组的大小。这样的用法有点类似于宏的用法,但相比于宏的优点在于,不同的对象可以有不同的N值,但宏却只能用 #define N num 这样的方式定义一个N值。
3、模板特化(Template Specialization)
模板特化是指对特定类型或值的情况,为通用模板提供特定的实现。通过模板特化,可以为特定的类型或值提供定制化的实现,以满足特殊需求
(1)全特化(Full Specialization)
全特化是指对模板中所有的模板参数都进行特化,为特定的类型或值提供完全定制的实现,全特化中所有的模板参数都被具体化,不留下泛化的部分。举例如下:
template <class T>
bool Is_Equal(const T& x, const T& y)
{
return x==y;
}
//针对char*类型的全特化
template<>
bool Is_Equal<char*>(char* x, char* y)
{
return strcmp(x,y)==0;
}
上面这段代码的模板函数包括了通用的版本和对针对模板参数传入时char*实现的全特化版本,在调用函数时如果模板参数的类型是char*,编译器便会去调用经过特化的Is_Equal函数。
(2)偏特化(Partial Specialization)
偏特化是指对模板中的部分模板参数进行特化,为特定的类型或值提供部分定制的实现,偏特化中只有部分的模板参数被具体化,而其他部分仍然保持泛化,以下是一个偏特化的类模板示例
//泛化的类模板
template<class T1, class T2>
class Example
{
Example ()
{
cout <<"调用了泛化的模板"<< endl;
}
}
//偏特化的类模板
template<>
class Example<int, T2>
{
Example ()
{
cout <<"调用了偏特化的模板"<< endl;
}
}
int main()
{
Example<int ,char> e;
}
在上面这段代码中,Example<int, char>中的int与偏特化模板Example<int ,T2>中的int进行了
配对,在调用构造函数时便会调用偏特化类模板中的构造函数。
四、使用模板的弊端
在C++中,模板类和模板函数是不能进行分离编译的,也就是说不能像一般的项目那样,把函数和类的声明都放在以.h为后缀的头文件,把函数的实现放在以.cpp为后缀的源文件中。原因是在链接过程中,编译器找不到模板函数的定义,因为在使用模板时,.h文件中的模板的声明知晓了模板参数的类型,但在.cpp文件中的模版不知道,所以.cpp中模板的定义没法实例化,也就没法在编译阶段生成一份经过实例化的具体的代码,这将导致程序在链接阶段找不到模板函数的实现而报链接错误。