定义模板
如果我们需要反复用到一些函数,这些函数仅仅只是参数类型不同。他们定义的函数体完全一样,这时候我们就希望有一个函数模板。放入不同的模具就能生成不同的函数,以此来实现我们想要的功能。假如我们没有这些模板我们会大量定义如下的重载函数:
int compare(const string &v1,const string &v2)
{
if(v1 < v2) return -1;
if(v2 < v1) retrun 1;
return 0;
}
int compare(const double &v1,const double &v2)
{
if(v1 < v2) return -1;
if(v2 < v1) retrun 1;
return 0;
}
函数模板
现在我们来定义一个函数模板:
template <typename T>
int compare(const T &v1,const T &v2)
{
if(v1 < v2) return -1;
if(v2 < v1) return 1;
return 0;
}
看上去很简单嘛,不是吗?这里用到了一个关键字template
,后面跟着模板参数列表。
-
实例化函数模板
- 有了函数模板编译器是如何替我们选择模具呢?当然是传入的实参来告诉编译器用那个模具。实际上当编译器推断出模板参数是什么类型时, 他会用模板函数来实例出一个我们将要用的函数。 模板类型参数
- 类型参数可以看作是类型说明符。例如:
template <typename T> T foo(T* p)
{
T tmp = *p;
return tmp;
}
// 有一点需要注意如果有多个不同的类型参数必须都标记出来
template <typename T, class U> calc (const T& ,const U&); (typename 和 class 没有区别)
-
非类型模板参数
- 有时候我们需要模板非类型参数是一个常量值,这就需要定义一个 非类型参数。那么什么情况我们需要定义一个非类型参数?当我们需要编写一个compare版本处理字符串字面常量。这种字面常量是const char 的数组。由于我们希望能比较不同长度的字符串字面常量。所以我们将自己的参数定义为数组的引用。N,M表示数组的长度。
template <unsigned N,unsigned M> compare(char (&p1)[N],char (&p2)[M])
{
return strcmp(p1,p2);
}
compare("hi","hello");
-
inline 和 constexpr 的函数模板
- 这里需要说明一下函数模板也可以被声明为inline 或 constexpr 的,和非模板函数一样。inline 或 constexpr 说明符放在函数返回类型值之前:
template <typename T> inline T min(const T&,const T&);
类模板
-
定义一个类模板
- 举一个例子:
template <typename T> class Blob {
public :
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
Blob();
Blob(std::initializer_list<T> il);
size_type size() const {
return data->size();
}
bool empty() const {
return data->empty();
}
void push_back(const T& t)
{
data->push_back(t);
}
void push_back(T&& t)
{
data->push_back(std::move(t));
}
void pop_back();
T& back();
T& opertor[](size_type i);
private:
std::shared_ptr<std::vector<T>> data;
void check(size_type i, const std::string& msg) const;
};
定义一个模板编译器并不能推断出模板参数类型,必须由我们自己在模板类名后面的尖括号内添加额外的信息。
-
类模板的成员函数
- 类模板的成员函数和其他类相同既可以定义在类模板内部,也可以定义在类模板外部。定义在外部时需要加关键字 template。举个例子:
template<typename T>
ret-type Blob<T>::member-name(parm-list)