1. Function templates
1、
Templates allow us to generate general functions that can accept parametersof any data type and return values of any type without the need to overload all possible data types. This
achieves the function of macros to a certain extent. Their prototype definition can be any of the following two types:
template <class identifier> function_declaration;
template <typename identifier> function_declaration
Is actually the same
To generate a template and return the larger of the two objects, we can write:
template <class GenericType>
GenericType GetMax (GenericType a, GenericType b) { return (a>b?a:b); }
Call template
function <type> (parameters);
int x,y;
GetMax <int> (x,y);
The template for the generic data type is called GenericType. Therefore, in the following function, GenericType becomes a valid data type, it is used to define the two parameters a and b, and is used as the return value type of the function GetMax.
GenericType still does not represent any specific data type; when the function GetMax is called, we can
use any valid data type to call it. This data type will be used as a pattern instead of where
GenericType appears in the function . The method of calling a template with a type pattern is as follows:
on <type> (parameters);
Here is an example:
//function template
#include <iostream.h>
template <class T>
T GetMax (T a, T b) {
T result;
result = (a>b)? a : b;
return (result);
}
int main () {
int i=5, j=6, k;
long l=10, m=5, n;
k=GetMax(i,j);
n=GetMax(l,m);
cout << k << endl;
cout << n << endl;
return 0;
}
result:
6
10
In this specific example, the generic type T is used as the parameter of the function GetMax, no need to specify <int> or
<long>, the compiler can also automatically detect the incoming data type, so we can also write this cases of child:
int i,j;
GetMax (i,j);
Because both i and j are of type int, the compiler will automatically assume that we want the function to be called as int. This implied
method is more useful and produces the same result:
2、
We can also make the template function accept two or more types of data, for example
template <class T>
T GetMin (T a, U b) { return (a<b?a:b); }
在这个例子中,我们的模板函数 GetMin() 接受两个不同类型的参数,并返回一个与第
一个参数同类型的对象。在这种定义下,我们可以这样调用该函数:
int i,j;
long l;
i = GetMin <int, long> (j,l);
或者,简单的用
i = GetMin (j,l);
虽然 j 和 l 是不同的类型。
二、类模板(Class templates)
我们也可以定义类模板(class templates),使得一个类可以有基于通用类型的成员,
而不需要在类生成的时候定义具体的数据类型,例如:
template <class T>
class pair {
T values [2];
public:
pair (T first, T second) {
values[0]=first;
values[1]=second;
}
};
上面我们定义的类可以用来存储两个任意类型的元素。例如,如果我们想要定义该类
的一个对象,用来存储两个整型数据 115 和 36 ,我们可以这样写:
pair<int> myobject (115, 36);
pair<float> myfloats (3.0, 2.18);
如果我们要在类之外定义它的一个成员函数,我们必须在每一函数前面加
template <... >
//class templates
#include <iostream.h>
template <class T>
class pair {
T value1, value2;
public:
pair (T first, T second) {
value1=first;
value2=second;
}
T getmax ();
};
template <class T>
T pair::getmax (){
T retval;
retval = value1>value2? value1 : value2;
return retval;
}
int main () {
pair myobject (100, 75);
cout << myobject.getmax();
return 0;
}
三、模板特殊化(Template specialization)
模板的特殊化是当模板中的 pattern有确定的类型时,模板有一个具体的实现。
例如假设我们的类模板 pair 包含一个取模计算(module operation)的函数,而我们希望这个函数只有当对象中存储的数据为整型(int)的时候才能工作,其他时候,我们需要这个函数总是返回 0。这可以通过下面的代码来实现:
//Template specialization
#include <iostream.h>
template <class T>
class pair {
T value1, value2;
public:
pair (T first, T second){
value1=first;
value2=second;
}
T module () {return 0;}
};
template <>
class pair <int> {
int value1, value2;
public:
pair (int first, int second){
value1=first;
value2=second;
}
int module ();
};
template <>
int pair<int>::module() {
return value1%value2;
}
int main () {
pair <int> myints (100,75);
pair <float> myfloats (100.0,75.0);
cout << myints.module() << '\n';
cout << myfloats.module() << '\n';
return 0;
}
结果:
25
0
由上面的代码可以看到,模板特殊化由以下格式定义:
template <> class class_name <type>
这个特殊化本身也是模板定义的一部分,因此,我们必须在该定义开头写 template
<>。而且因为它确实为一个具体类型的特殊定义,通用数据类型在这里不能够使用,
所以第一对尖括号<> 内必须为空。在类名称后面,我们必须将这个特殊化中使用的
具体数据类型写在尖括号<>中。
当我们特殊化模板的一个数据类型的时候,同时还必须重新定义类的所有成员的特殊
化实现(如果你仔细看上面的例子,会发现我们不得不在特殊化的定义中包含它自己
的构造函数 constructor,虽然它与通用模板中的构造函数是一样的)。这样做的原因
就是特殊化不会继承通用模板的任何一个成员。
四、模板的参数值(Parameter values for templates)
除了模板参数前面跟关键字 class 或 typename 表示一个通用类型外,函数模板和类模
板还可以包含其它不是代表一个类型的参数,例如代表一个常数,这些通常是基本数
据类型的。例如,下面的例子定义了一个用来存储数组的类模板:
//array template
#include <iostream.h>
template <class T, int N>
class array {
T memblock [N];
public:
void setmember (int x, T value);
T getmember (int x);
};
template <class T, int N>
void array<T,N>::setmember (int x, T value) {
memblock[x]=value;
}
template <class T, int N>
T array<T,N>::getmember (int x) {
return memblock[x];
}
int main () {
array <int,5> myints;
array <float,5> myfloats;
myints.setmember (0,100);
myfloats.setmember (3,3.1416);
cout << myints.getmember(0) << '\n';
cout << myfloats.getmember(3) << '\n';
return 0;
}
结果:
100
3.1416
我们也可以为模板参数设置默认值,就像为函数参数设置默认值一样。
下面是一些模板定义的例子:
template <class T> //最常用的:一个 class 参数。
template <class T, class U> //两个 class 参数
template <class T, int N> //一个 class 和一个整数。
template <class T = char> //有一个默认值。
template <int Tfunc (int)> //参数为一个函数。