目录
一.函数模板
1.普通模板
格式:
// 声明
template <typename T> // or class T
void Swap(T &a, T &b);
// 定义
template <typename T> // or class T
void Swap(T &a, T &b)
{
T temp; // temp a variable of type T
temp = a;
a = b;
b = temp;
}
第一行指出要建立一个模板,并将类型命名为T,关键字 template 和 typename 是必须的;
typename 也可以换成关键字 class 来创建模板:
// 声明
template <class T> // or class T
void Swap(T &a, T &b);
// 定义
template <class T> // or class T
void Swap(T &a, T &b)
{
T temp; // temp a variable of type T
temp = a;
a = b;
b = temp;
}
调用:
int i = 10;
int j = 20;
Swap(i,j);
double x = 24.5;
double y = 81.7;
Swap(x,y);
第一个 Swap 函数接收了两个 int 参数,因此编译器生成 int 版本,也就是用 int 替换所有的 T ,生成下面的定义:
void Swap(int &a, int &b)
{
int temp; // temp a variable of type int
temp = a;
a = b;
b = temp;
}
第二个 Swap 函数接收了两个 double 参数,因此编译器生成 double 版本,也就是用 double 替换所有的 T ,生成下面的定义:
void Swap(double &a, double &b)
{
double temp; // temp a variable of type double
temp = a;
a = b;
b = temp;
}
当然,模板也可以用在函数重载中:
template <typename T> // original template
void Swap(T &a, T &b);
template <typename T> // new template
void Swap(T *a, T *b, int n);
调用时会根据输入参数情况,调用相应函数。
2.模板的具体化与实例化
具体化:
对于给定的函数名,有非模板、普通模板、显式具体化模板、重载版本;
优先级为:非模板 > 显式具体化模板 > 普通模板
而显示具体化作用是生成指定类型的模板:
struct job
{
...
}
//普通模板
template <typename T> // or class T
void Swap(T &a, T &b);
//具体化模板
template <> void Swap<job>(job &j1, job &j2);
这时就生成一个类型为 job 的模板,当参数为 job 时,将优先调用:
double u,v;
Swap(u,v);
job a,b;
Swap(a,b);
上述程序第一次将调用普通模板,第二次会调用 job 类型模板;
具体化的意义在于,可以以某个类型定义单独的函数,如下所示:
template <typename T>
void Swap(T &a, T &b) // general version
{
T temp;
temp = a;
a = b;
b = temp;
}
// swaps just the salary and floor fields of a job structure
template <> void Swap<job>(job &j1, job &j2) // specialization
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
此时,job 的具体化模板可以单独修改某个成员变量;
对于普通模板,因为事先不知道是什么类型,所以不方便修改,比如int型就没有成员变量这一说;
实例化:
格式:
//普通模板
template <typename T>
void Swap(T &a, T &b);
//具体化模板
template <> void Swap<job>(job &j1, job &j2);
//实例化模板
template void Swap<char>(char &j1, char &j2);
与具体化区别在于:
1、template后面少了一个 <> ;
2、调用时使用普通模板生成 char 类型的模板,所以不用单独定义;而具体化是要单独定义的;
3、直接在调用前声明,后面即可使用,不用像具体化那样声明、定义都要有,使用如下:
int main(void)
{
template void Swap<char>(char &j1, char &j2);
char a,b;
Swpa(g,h);
}
二.类模板
1.定义类模板
示例:
// stacktp.h -- a stack template
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Type items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & item); // add item to stack
bool pop(Type & item); // pop top into item
};
template <class Type>
Stack<Type>::Stack()
{
top = 0;
}
template <class Type>
bool Stack<Type>::isempty()
{
return top == 0;
}
template <class Type>
bool Stack<Type>::isfull()
{
return top == MAX;
}
template <class Type>
bool Stack<Type>::push(const Type & item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::pop(Type & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
#endif
(1)和模板函数一样,模板类以下面代码开头:
template <class Type>
或者:
template <typename Type>
当模板被调用时,Type 将被具体的类型值(int、string)取代;
(2)成员变量类型都用 Type 声明:
Type items[MAX];
(3)成员函数都以相同的模板声明打头
template <class Type>
bool Stack<Type>::isempty()
{
return top == 0;
}
注意这里将类限定符从 Stack:: 改为 Stack<Type>:: 。
如果是在类中就定义了成员函数的方法(内联定义),则可以省略模板前缀和类限定符;
不能将模板成员函数放在独立的实现文件中,因为模板不是函数,不能单独编译,最简单的方法是将所有的模板信息放在一个头文件中,在使用这些模板时包含该头文件即可。
2.使用类模板
示例:
// stacktem.cpp -- testing the template stack class
#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"
using std::cin;
using std::cout;
int main()
{
Stack<std::string> st; // create an empty stack
char ch;
std::string po;
cout << "Please enter A to add a purchase order,\n"
<< "P to process a PO, or Q to quit.\n";
while (cin >> ch && std::toupper(ch) != 'Q')
{
while (cin.get() != '\n')
continue;
if (!std::isalpha(ch))
{
cout << '\a';
continue;
}
switch(ch)
{
case 'A':
case 'a': cout << "Enter a PO number to add: ";
cin >> po;
if (st.isfull())
cout << "stack already full\n";
else
st.push(po);
break;
case 'P':
case 'p': if (st.isempty())
cout << "stack already empty\n";
else {
st.pop(po);
cout << "PO #" << po << " popped\n";
break;
}
}
cout << "Please enter A to add a purchase order,\n"
<< "P to process a PO, or Q to quit.\n";
}
cout << "Bye\n";
// cin.get();
// cin.get();
return 0;
}
(1)实例化模板:
Stack<std::string> st;
此时编译器将生成一个类声明和类方法,且用 std::sting 代替 Type;
(2)使用模板方法
实例化模板后,对象将像使用普通类方法那样使用模板类方法:
st.isfull()
3.深入探讨模板类(略)
4.数组模板示例和非类型参数
示例:
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
#include <cstdlib>
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & v);
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
for (int i = 0; i < n; i++)
ar[i] = v;
}
template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
if (i < 0 || i >= n)
{
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
template <class T, int n>
T ArrayTP<T,n>::operator[](int i) const
{
if (i < 0 || i >= n)
{
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
#endif
(1)非类型参数
template <class T, int n>
关键字 class/typename 指出 T 为类型参数;
int 指出 n 为类型为 int ,这种参数成为非类型 / 表达式参数;
(2)数组模板
用 n 指定数组大小,T 指定数组类型;
T ar[n];
之后在成员函数中使用 ar[i] ,就相当于使用普通的一维数组;
5.模板多功能性
(1)递归使用模板
ArrayTP< ArrayTP<int,5>, 10> twodee;
这使得 twodee 是一个包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组,等价常规数组为:int twodee[10][5];
使用如下:
// twod.cpp -- making a 2-d array
#include <iostream>
#include "arraytp.h"
int main(void)
{
using std::cout;
using std::endl;
ArrayTP<int, 10> sums;
ArrayTP<double, 10> aves;
ArrayTP< ArrayTP<int,5>, 10> twodee;
int i, j;
for (i = 0; i < 10; i++)
{
sums[i] = 0;
for (j = 0; j < 5; j++)
{
twodee[i][j] = (i + 1) * (j + 1);
sums[i] += twodee[i][j];
}
aves[i] = (double) sums[i] / 10;
}
for (i = 0; i < 10; i++)
{
for (j = 0; j < 5; j++)
{
cout.width(2);
cout << twodee[i][j] << ' ';
}
cout << ": sum = ";
cout.width(3);
cout << sums[i] << ", average = " << aves[i] << endl;
}
cout << "Done.\n";
// std::cin.get();
return 0;
}
(2)使用多个类型参数
// pairs.cpp -- defining and using a Pair template
#include <iostream>
#include <string>
template <class T1, class T2>
class Pair
{
private:
T1 a;
T2 b;
public:
T1 & first();
T2 & second();
T1 first() const { return a; }
T2 second() const { return b; }
Pair(const T1 & aval, const T2 & bval) : a(aval), b(bval) { }
Pair() {}
};
template<class T1, class T2>
T1 & Pair<T1,T2>::first()
{
return a;
}
template<class T1, class T2>
T2 & Pair<T1,T2>::second()
{
return b;
}
int main()
{
using std::cout;
using std::endl;
using std::string;
Pair<string, int> ratings[4] =
{
Pair<string, int>("The Purpled Duck", 5),
Pair<string, int>("Jaquie's Frisco Al Fresco", 4),
Pair<string, int>("Cafe Souffle", 5),
Pair<string, int>("Bertie's Eats", 3)
};
int joints = sizeof(ratings) / sizeof (Pair<string, int>);
cout << "Rating:\t Eatery\n";
for (int i = 0; i < joints; i++)
cout << ratings[i].second() << ":\t "
<< ratings[i].first() << endl;
cout << "Oops! Revised rating:\n";
ratings[3].first() = "Bertie's Fab Eats";
ratings[3].second() = 6;
cout << ratings[3].second() << ":\t "
<< ratings[3].first() << endl;
// std::cin.get();
return 0;
}
模板包含多个类型参数:
template <class T1, class T2>
在使用时提供多个类型:
Pair<string, int> ratings;
(3)默认类型模板参数
template <class T1, class T2 = int>
class Topo
{
...
}
这样,如果省略了T2的值,编译器将使用 int;
6.模板具体化
(1)隐式实例化
前面所用的都是隐式实例化:
ArrayTP<int, 100> stuff;
编译器在需要对象之前,不会生成类的隐式实例化,但调用这一句,会生成相应的类定义,并根据类定义生成一个对象;
(2)显式实例化
使用关键字,且声明在类模板定义所在命名空间中,并指出所需类型:
template class ArrayTP<int, 100>;
此时虽然没有创建对象,但编译器会生成相应的类声明及其方法;
(3)显式具体化
针对不同类型定义的具体化模板,该模板为具体类型的模板,而不是上面所说的泛型定义的模板;
当编译器请求匹配实例化时,将优先使用具体化模板,如下为专供 const char *类型的模板:
template <> class SorteArray<const char char *>
{
}
其中的方法可以不同于通用模板的方法,这样特定类型调用时,可以使用这个类型模板提供的特殊方法;
如下所示,当请求 const char * 类型的 SorteArray 模板时,编译器将使用上述专用的定义,而不是通用模板定义:
SorteArray<int> scores; //使用通用模板
SorteArray<const char *> dates; //使用特定类型模板
(4)部分具体化
部分具体化可以给模板的通用类型参数之一指定具体类型:
//通用模板
template <class T1, class T2>
class Pair
{
...
}
//部分具体化模板
template <class T1>
class Pair <T1, int>
{
...
}
关键字 template 后面 <> 是没有被具体化的类型参数;
如果指定所有类型,则 <> 将为空,此时就是显式具体化:
template <>
class Pair <int, int>
{
...
}
以上多种模板,在实际使用时,编译器将优先使用具体化程度最高的模板:
Pair<double, double> p1; //使用通用模板
Pair<double, int> p1; //使用部分具体化模板
Pair<int, int> p1; //使用显式具体化模板
7.成员模板
示例:
// tempmemb.cpp -- template members
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class beta
{
private:
template <typename V> // nested template class member
class hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
hold<T> q; // template object
hold<int> n; // template object
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // template method
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
void Show() const { q.show(); n.show();}
};
int main()
{
beta<double> guy(3.5, 3);
cout << "T was set to double\n";
guy.Show();
cout << "V was set to T, which is double, then V was set to int\n";
cout << guy.blab(10, 2.3) << endl;
cout << "U was set to int\n";
cout << guy.blab(10.0, 2.3) << endl;
cout << "U was set to double\n";
cout << "Done\n";
// std::cin.get();
return 0;
}
(1)该模板类将另一个模板类作为其成员:
template <typename T>
class beta
{
private:
template <typename V>
class hold
{
}
}
因为 hold 模板是放在私有部分的,所以只能在 beta 类中访问,beta 类使用 hold 模板声明了两个数据成员:
hold<T> q; // template object
hold<int> n; // template object
(2)该模板类将另一个模板函数作为其成员:
template <typename T>
class beta
{
public:
template<typename U>
U blab(U u, T t)
{
return (n.Value() + q.Value()) * u / t;
}
}
可以看到,现在整个模板一共有三个参数:T、V、U
(3)调用
在main函数中,下述方法将 T 设置为 double :
beta<double> guy(3.5, 3);
下述方法调用的第一个参数将 U 类型设置为 int:
guy.blab(10, 2.3)
下述方法 V 类型设置为 T 类型,也即 double:
guy.Show();
因为 show() 将调用 q.show(),q 在实例化为 hold<double> q 时会将 V 设置为 double;
同理,下述方法 V 类型设置为 double 类型:
hold<int> n;