模板类可以使用多个类型参数:
看一个例子:
// pairs.cpp
#include <iostream>
#include <string>
template <class T1, class T2>
class Pair
{
private:
T1 a;
T2 b;
public:
// 这两个返回引用的实现虽然逻辑和后面的返回对象的实现是一样的
// 但是不能删除, 如果删除后, 在下面的main方法中调用ratings[3].first() = "Berties"; 就会出现错误:
// lvalue required as left operand of assignment
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>("first", 5),
Pair<string, int>("second", 6),
Pair<string, int>("third", 7),
Pair<string, int>("four", 8)
};
int joints = sizeof(ratings) / sizeof(Pair<string, int>);
cout << "Rating:\t Eatery " << endl;
for(int i = 0; i < joints; i++)
{
cout << ratings[i].second() << ": \t " << ratings[i].first() << endl;
}
cout << "Oops! Revised rating: " << endl;
ratings[3].first() = "Berties";
ratings[3].second() = 9;
cout << ratings[3].second() << ": \t" << ratings[3].first() << endl;
return 0;
}
运行结果为:
程序很简单, 唯一需要注意的就是两个返回引用的函数的return虽然和返回对象的return一模一样, 但是这两个返回引用的方法不能省略, 至于原因看一下注释
默认类型模板参数
可以为类型参数提供默认值:
template<class T1, class T2 = int> class Topo {...};
这样, 如果省略T2的值, 编译器将使用int:
// T1和T2都是double
Topo<double, double> m1;
// T1是double, T2是int
Topo<double> m2;
模板的具体化
1.隐式实例化
ArrayTP<int, 100> stuff;
编译器在需要对象之前, 不会生成类的隐式实例化:
// 只是声明了一个指针, 没有对象需要创建
ArrayTP<double, 30> * pt;
// 需要创建一个对象
// 编译器生成类定义, 并根据该定义创建一个对象.
pt = new ArrayTP<double, 30>;
2.显示实例化
当使用关键字template并指出所需类型来声明类时, 编译器将生成类声明的显示实例化. 声明必须位于模板定义所在的名称空间中:
// 虽然没有创建和提及类对象, 编译器也将生成类声明(包括方法定义)
template class ArrayTP<string, 100>;
3.显示具体化
显示具体化是特定类型(用于替换模板中的泛型)的定义.有时候可能需要为特殊类型实例化时, 对模板进行修改使其行为不同. 例如, 有一个用于表示排序后数组的类定义了一个模板:
template <typename T>
class SortedArray
{
...
};
假设上述模板使用>运算符来对值进行比较, name对于数字没有问题, 但是如果T表示的是const char*这种的字符串, 将会失去作用. 在这种情况下, 可以提供一个显示板具体化, 浙江采用为具体类型定义的模板, 而不是为泛型类型定义的模板. 当具体化模板和通用模板都与实例化请求匹配时, 编译器将使用具体化版本:
具体化类模板定义格式如下:
template<> class Classname<specialized-type-name> {...};
使用新的表示法提供一个专供const char*类型使用的SortedArray模板, 可以使用类似下面的代码:
template<> class SortedArray<const char *>
{
...
};
那么, 当再请求const char*类型的SortedArray模板时, 编译器将使用专用的定义, 而不是通用的模板定义.
// 使用通用的模板定义
SortedArray<int> scores;
// 使用专用的模板定义
SortedArray<const char *> dates;
4.部分具体化
C++还允许部分具体化, 即部分闲置模板的通用性, 例如: 部分具体化可以给类型参数之一指定具体的类型:
// 通用的模板定义
template <class T1, class T2> class Pair {...};
部分具体化的格式如下:
// 部分具体化
template<class T1> class Pair<T1, int> {...};
关键字template后面的<>声明的是没有被具体化的类型参数. 因此, 上述第二个声明T2具体化为int, 但T1保持不变. 注意如果指定所有的类型, 则<>内将为空, 这将导致显示具体化
template<> class Pair<int, int> {...};
如果有多个模板可供选择, 编译器将使用具体化程度最高的模板, 例如:
// 使用通用的模板定义
Pair<double, double> p1;
// 使用Pair<T1, int>部分具体化
Pair<double, int> p2;
// 使用显示具体化Pair<int, int>
Pair<int, int> p3;
也可以通过为指针提供特殊版本来部分具体化现有的模板:
template<class T>
class Feeb {...};
template<class T *>
class Feeb {...}
如果提供的类型不是指针, 则编译器将使用通用版本, 如果提供的是指针, 编译器将使用指针具体化版本:
// 通用版本
Feeb<char> fb1;
// 指针版本
Feeb<char *> fb2;
如果没有进行部分具体化, 则第二个声明将使用通用模板, 将T转换为char* 类型, 如果进行了部分具体化, 则第二个声明将使用具体化版本, 将T转换为char:
template<class T1, class T2, class T3> class Trio {...};
template<class T1, class T2> class Trio<T1, T2, T2> {...};
template<class T1> class Trio<T1, T1*, T1*> {...};
有下面的代码:
// 调用的是通用模板
Trio<int, short, char *> t1;
// Trio<T1, T2, T2>
Trio<int, short> t2;
// 调用的是Trio<T1, T1*, T1*>
Trio<char, char *, char *> t3;
成员模板
就是将模板当作类的成员使用, 来看一个例子:
// tempmemb.cpp
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class beta
{
private:
// 模板类中嵌套的模板成员
// 由于模板类是私有的, 因此只能再beta内使用
template <typename V>
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;
hold<int> n;
public:
beta(T t, int i):q(t), n(i) {}
// 模板方法
template<typename U>
U blab(U u, T t) {return (n.Value() + q.Value()) * u / t;}
void Show() const {q.show(); n.show();}
};
int main()
{
// 由于T表示double, 因此q的类型为hold<double>
beta<double> guy(3.5, 3);
cout << "T was set to double " << endl;
guy.Show();
cout << "V was set to T, which is double, then V was set to int" << endl;
// 将U设置为int类型, 由于blab返回的也是U, 因此虽然里面是double结果, 但是返回会强转成int
cout << guy.blab(10, 2.5) << endl;
cout << "U was set to int" << endl;
// U设置成了double, 因此返回的结果是double类型的
cout << guy.blab(10.0, 2.5) << endl;
cout << "U was set to double " << endl;
cout << "Done" << endl;
return 0;
}
程序运行结果为:
程序中需要注意的地方都写有注释
将模板用作参数
模板可以包含类型参数(如typename T)和非类型参数(如 int n). 模板还可以包含本身就是模板的参数:
template<template<typename T> class Thing>
class Crab
模板参数是template<typename T>class Thing, 其中template<typename T>class 是类型, Thing是参数. 这意味着, 假设有下面的声明:
Crab<King> legs;
为使上述声明被接受, 模板参数King必须是一个模板类, 其声明与模板参数Thing的声明匹配:
template<typename T>
class King{...};
来看一个完整的例子:
首先是Stack的模板类:
// stacktp.h
#ifndef STACKTP_H_
#define STACKTP_H_
// 模板类
template <class Type>
class Stack
{
private:
enum {MAX = 10};
Type items[MAX];
int top;
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & tiem);
bool pop(Type & 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
接着是将模板作为参数使用:
// tempparm.cpp
#include <iostream>
#include "stacktp.h"
template <template<typename T> class Thing>
class Crab
{
private:
Thing<int> s1;
Thing<double> s2;
public:
Crab(){};
bool push(int a, double x) {return s1.push(a) && s2.push(x);}
bool pop(int & a, double & x) {return s1.pop(a) && s2.pop(x);}
};
int main()
{
using std::cout;
using std::endl;
using std::cin;
// Stack必须匹配template<typename T>
Crab<Stack> nebula;
int ni;
double nb;
cout << "Enter int double pairs, such as 4 3.5(0 0-to end):" << endl;
while(cin >> ni >> nb && ni > 0 && nb > 0)
{
if(!nebula.push(ni, nb))
break;
}
while(nebula.pop(ni, nb))
cout << ni << ", " << nb << endl;
cout << "Done." << endl;
return 0;
}
程序运行结果为: