一、函数模板
1、定义
建立一个通用函数,它所用到的数据的类型
(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替
(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。
2、举例
template<typename T> //T就是一个虚拟的类型,typename关键字也可以用class 关键字替换
bool compare(T a, T b)
{
cout << "template compare" << endl;
cout << typeid(T).name() << endl;
return a > b;
}
在函数模板中,数据的值和类型都被参数化了,
发生函数调用时编译器会根据传入的实参来推演形参的值和类型。
函数模板除了支持值的参数化,还支持类型的参数化。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
3、多参数的函数模板
template<typename T1,typename T2>//模板说明的类型参数必须在函数定义中至少出现一次
void Fun(T1 a)
{
cout<<"aaa"<<endl;
}
- 1
- 2
- 3
- 4
- 5
5、类型参数和非类型参数
template<typename T,int b>//T为类型参数,b为非类型参数
该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值
- 1
- 2
- 3
6、模板参数应该注意的地方
- 模板形参遵循常规名字屏蔽规则。与全局作用域中声明的对象、函数或类型 同名的模板形参会屏蔽全局名字
typedef double T;
template <class T> T fun(const T &a, const T &b)
{
T tmp = a;
return tmp;
}
//将 T 定义为 double 的全局类型型别名将被名为 T 的类型形参所屏蔽,因 此,tmp 不是 double 型,相反,tmp 的类型是绑定到模板形参的任意类型。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 用作模板形参的名字不能在模板内部重用。
template <class T> T calc(const T &a, const T &b)
{
typedef double T; // error: redeclares template parameter T
T tmp = a;
return tmp;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 模板形参的名字只能在同一模板形参表中使用一次
template <class V, class V> V calc(const V&, const V&) ;
// error: illegal reuse of template parameter name V
- 1
- 2
二、模板函数
1、定义
模板函数的生成就是将函数模板的类型形参实例化
的过程。
2、举例
bool compare<int>(int a, int b)
{
cout << "template compare" << endl;
cout << typeid(int).name() << endl;
return a > b;
}
- 1
- 2
- 3
- 4
- 5
- 6
3、模板函数与函数重载的区别
模板函数类似于重载函数,但两者有很大区别:
函数重载时,每个函数体内可以执行不同的动作
但同一个函数模板实例化后的模板函数都必须执行相同的动作
- 1
- 2
三、模板函数的特例化
当我们定义的函数模板对于大多数的类型适用,但对于一些特殊函数尤其是字符串相关的一些操作时,就可能不太适用。因此,我们必须实现模板函数的特例化版本。
// compare char*类型的特例化版本
template<>
bool compare<char*>(char * a, char *b)
{
cout << "compare<char*>" << endl;
return strcmp(a, b);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
template<>
bool compare<int>(int a, int b)
{
cout << "compare<int>" << endl;
return a > b;
}
类模板的部分特例化
- 什么是部分特例化(partial specialization)?
“针对模板参数更进一步的条件限制所设计出来的一个特化版本” (摘自《泛型思维》)
//原始的,最通用版本,允许接收T为任何类型
template<typename T>
class C { ... };
//部分特例化,此版本仅适用于指针类型
template<typename T>
class C<T *> { ... };
注:只有类模板才支持部分特例化,函数模板特例化时必须为原模板中每个模板参数都提供实参。
- 进一步介绍:
类模板的部分特例化版本在本质上还是一个模板。
当我们定义类模板的部分特例化版本时,我们首先定义模板参数(就是template<>中的内容)。特例化版本的模板参数只包含原模板中那些还未确定类型的模板参数。
在类名之后的<>中,我们要为特例化的模板参数指定实参。(注意:这些实参与原模板中的参数按位置对应)
例如:
//general template
template<class T1, class T2>
class Pair { ... };
//specialization with T2 set to int
template<class T1>
class Pair<T1, int> { ... };
上述第二个声明将T2具体化为int,但T1保持不变。
到此,你可能会对文章开始的那个例子感到困惑,在那个例子中特例化版本的模板参数的数目与原始模板相同,怎么叫部分特例化?
其实,部分特例化版本的模板参数列表是原始模板参数列表的一个子集或者是一个特例化版本。在第一个例子中,就是使用指针提供特殊版本来部分实例化现有的模板。
- 使用部分特例化的类模板
对于class C:
C<char> c1; //use general C template, T is char
C<char *> c2; //use C<T *> partial specialization, T is char
对于class Pair:
Pair<double, double> p1; //use general Pair template
Pair<double, int> p2; //use Pair<T1, int> partial specialization
注:如果有多个模板可供选择,编译器将使用特例化程度最高的模板。
- 补充:
如果我们对所有的模板参数指定类型,则<>内将为空,这将导致全特例化版本,又叫显示特例化(explicit specialization)。
例如:
//specialization with T1 and T2 set to int
template<>
class Pair<int, int> { ... };
//use Pair<int, int> explicit specialization
Pair<int, int> p3;
参考文献:
《C++ Primer》第五版