【C++】C++模板【转】

一、定义类模板

模板类以下面这样的代码开头∶

template <class Type>

关键字 template 告诉编译器,将要定义一个模板。尖括号中的内容相当于函数的参数列表。可以把关
键字class看作是变量的类型名,该变量接受类型作为其值,把Type看作是该变量的名称。

这里使用class 并不意味着 Type 必须是一个类;而只是表明Type是一个通用的类型说明符,在使用模板时,将使用实际的类型替换它。较新的 C++实现允许在这种情况下使用不太容易混淆的关键字 typename代替 class∶

template <typename Type> // newer choice

可以使用自己的泛型名代替 Type,其命名规则与其他标识符相同。当前流行的选项包括 T 和 Type,
我们将使用后者。当模板被调用时,Type 将被具体的类型值(如 int 或 string)取代。

程序清单 14.13列出了类模板和成员函数模板。知道这些模板不是类和成员函数定义至关重要。它们
是C++编译器指令,说明了如何生成类和成员函数定义。模板的具体实现—如用来处理 string 对象的栈类——被称为实例化(instantiation)或具体化(specialization)。不能将模板成员函数放在独立的实现文件中(以前,C++标准确实提供了关键字 export,让您能够将模板成员函数放在独立的实现文件中,但支持该关键字的编译器不多;C++11不再这样使用关键字export,而将其保留用于其他用途)。由于模板不是函数,它们不能单独编译。模板必须与特定的模板实例化请求一起使用。为此,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。

程序清单14.13 stacktp.h

// 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

二、使用模板类

仅在程序句含模板并不能生成模板类。而必以须请求实例化。为此。需要声明一个类型为模板类的对象方法是使用所需的具体类型替换泛型名。例如,下面的代码创建两个栈,一个用于存储 int,另一个用于存储 string 对象∶

Stack<int> kernels; // create a stack of ints
Stack<string> colonels;// create a stack of string objects

看到上述声明后,编译器将按 Stack模板来生成两个独立的类声明和两组独立的类方法。类声
明 Stack将使用 int 替换模板中所有的 Type,而类声明 Stack将用string 替换 Type。当然,使用的算法必以须与类型致。例如。Stack类假设可以将一个项目赋给另个项目。这种假设对干基本类型、结构和类来说是成立的(除非将赋值运算符设置为私有的),但对于数组则不成立。

泛型标识符——例如这里的 Type——称为类型参数(type parameter),这意味着它们类似于变量,但赋给它们的不能是数字,而只能是类型。因此,在 kernel声明中,类型参数Type 的值为 int。

注意,必须显式地提供所需的类型,这与常规的函数模板是不同的,因为编译器可以根据函数的参数
类型来确定要生成哪种函数∶

template <class T>
void simple(T t) { cout << t << '\n';}

simple(2); 		// generate void simple(int)
simple("two");。// generate void simple(const char *)

三、模板的具体化

类模板与函数模板很相似。因为可以有隐式实例化、显式实例化和显式具体化。它们统称为具体化(specialization)。模板以泛型的方式描述类,而具体化是使用具体的类型生成类声明。

3.1 隐式实例化

到目前为止,本章所有的模板示例使用的都是隐式实例化(implicit instantiation),即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板提供的处方生成具体的类定义;

ArrayTP<int,100> stuff;// implicit instantiation

编译器在需要对象之前,不会生成类的隐式实例化∶

ArrayTP<double,30>* pt;// a pointer,no object needed yet
pt = new ArrayTP<double,30>;// now an object is needed

第二条语句导致编译器生成类定义,并根据该定义创建一个对象。

3.2 显式实例化

当使用关键字 template 并指出所需类型来声明类时,编译器将生成类声明的显式实例化(explicit instantiation)。声明必须位于模板定义所在的名称空间中。例如,下面的声明将 ArrayTP<string,100>声明为一个类∶

template class ArrayTP<string,100>;// generate ArrayTP<string,100> class

在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。

3.3 显式具体化

显式具体化(explicit specialization)是特定类型(用于替换模板中的泛型)的定义。有时候,可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同。在这种情况下,可以创建显式具体化。例如,假设已经为用于表示排序后数组的类(元素在加入时被排序)定义了一个模板∶

template <typename T>
class SortedArray
{
	...// details omitted :
}

另外,假设模板使用>运算符来对值进行比较。对于数字,这管用;如果 T表示一种类,则只要定义了T∶∶operator>()方法,这也管用;但如果T是由 const char *表示的字符串,这将不管用。实际上,模板倒是可以正常工作,但字符串将按地址(按照字母顺序)排序。这要求类定义使用 strcmp(),而不是>来对值进行比较。在这种情况下,可以提供一个显式模板具体化,这将采用为具体类型定义的模板,而不是为泛型定义的模板。当具体化模板和通用模板都与实例化请求匹配时,编译器将使用具体化版本。

具体化类模板定义的格式如下;

template <> class Classname<specialized-type-name>{ ...};

早期的编译器可能只能识别早期的格式,这种格式不包括前缀 template<>

class Classname<specialized-type-name>{ ...};

要使用新的表示法提供一个专供 const char*类型使用的 SortedArray模板,可以使用类似于下面的代码∶

template <> class SortedArray<const char char *>
{
	 ...// details omitted
}

其中的实现代码将使用strcmp()(而不是>)来比较数组值。现在,当请求 const char *类型的 SortedArray模板时,编译器将使用上述专用的定义,而不是通用的模板定义∶

SortedArray<int> scores; // use general definition
SortedArray<const char *> dates; // use specialized definition
3.4 部分具体化

C++还允许部分具体化(partial specialization),即部分限制模板的通用性。例如,部分具体化可以给类型参数之一指定具体的类型∶

// general template
template <class T1,class T2> class Pair {...);
// specialization with T2 set to int
template <class T1> class Pair<T1,int>{...);

关键字 template 后面的<>声明的是没有被具体化的类型参数。因此,上述第二个声明将 T2 具体化为int,但T1保持不变。注意,如果指定所有的类型,则<>内将为空,这将导致显式具体化∶

// specialization with T1 and T2 set to int
template <> class Pair<int,int>{...};

如果有多个模板可供选择,编译器将使用具体化程度最高的模板。给定上述三个模板,情况如下∶

Pair<double,double> pl;	// use general Pair template
Pair<double,int> p2;	// use Pair<T1,int> partial specialization
Pair<int,int> p3; 		// use Pair<int,int> explicit specialization

也可以通过为指针提供特殊版本来部分具体化现有的模板∶

template<class T>	// general version
class Feeb{ ...};
template<class T*>	// pointer partial specialization 
class Feeb{ ...};	// modified code 

如果提供的类型不是指针,则编译器将使用通用版本;如果提供的是指针,则编译器将使用指针具体化版本∶

Feeb<char> fb1;		// use general Feeb template,T is char 
Feeb<char*> fb2;	// use Feeb T* specialization,T is char

如果没有进行部分具体化,则第二个声明将使用通用模板,将 T转换为char *类型。如果进行了部分
具体化,则第二个声明将使用具体化模板,将T转换为char。

部分具体化特性使得能够设置各种限制。例如,可以这样做∶

// general template
template<class T1, class T2, class T3> class Trio {...};

// specialization with T3 set to T2
template <class T1, class T2> class Trio<T1,T2,T2> {...};

// specialization with T3 and T2 set to T1*
template <class T1> class Trio<T1,T1*,T1*> {...};

给定上述声明,编译器将作出如下选择∶

Trio<int, short, char *>tl;		// use general template
Trio<int, short> t2;			// use Trio<T1,T2,T2>
Trio<char, char*, char*> t3;	// use Trio<T1,T1*,T1*>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值