模板与泛型编程
--模板特化[续]
三、特化成员而不特化类
除了特化整个模板之外,还可以只特化push和pop成员。我们将特化push成员以复制字符数组,并且特化pop成员以释放该副本使用的内存:
template<> void Queue<const char *>::push(const char *const &val) { char *new_item = new char[sizeof(val) + 1]; strncpy(new_item,val,sizeof(val)+1); QueueItem<const char *> *pt = new QueueItem<const char *>(new_item); if(empty()) { head = tail = pt; } else { tail -> next = pt; tail = pt; } } template<> void Queue<const char *>::pop() { QueueItem<const char *> *p = head; delete head -> item; head = head -> next; delete p; }
类类型Queue<constchar*> 将从通用类模板定义实例化而来,而push和pop函数例外。调用Queue<constchar*> 对象的push或 pop函数时,将调用特化版本;调用任意其他成员时,将从类模板为constchar* 实例化一个通用版本。
特化声明
成员特化的声明与其他函数模板特化一样,必须以空的模板形参表开头:
template <> void Queue<const char *>::push(const char *const &); template <> void Queue<const char *>::pop();
这些声明应放在Queue类的头文件中。
四、类模板的部分特化
如果类模板有一个以上的模板形参,我们也许想要特化某些模板形参而非全部。使用类模板的部分特化可以实现这一点:
template <class T1,class T2> class some_templates { //... }; template <class T1> class some_templates<T1,int> { //... };
类模板的部分特化本身也是模板。部分特化的定义看来像模板定义,这种定义以关键字template开头,接着是由尖括号(<>)括住的模板形参表。部分特化的模板形参表是对应的类模板定义形参表的子集。some_template的部分特化只有一个名为T1的模板类型形参,第二个模板形参T2的实参已知为int。部分特化的模板形参表只列出未知模板实参的那些形参。
使用类模板的部分特化
类模板的名字后面必须接着模板实参列表,前面例子中,模板实参列表是<T1,int>。因为第一个模板形参的实参值未知,实参列表使用模板形参名T1作为占位符,另一个实参是类型int,为int而部分特化模板。
像任何其他类模板一样,部分特化是在程序中使用时隐式实例化:
some_templates<int,string> foo; //使用泛型版本 some_templates<string,int> bar; //使用特化版本
注意第二个变量的类型,形参为string和 int的some_template,既可以从普通类模板定义实例化,也可以从部分特化实例化。为什么选择部分特化来实例化该模板呢?当声明了部分特化的时候,编译器将为实例化选择最特化的模板定义,当没有部分特化可以使用的时候,就使用通用模板定义。foo的实例化类型与提供的部分特化不匹配,因此,foo的类型必然从通用类模板实例化。部分特化只用于实例化第二个类型为int的some_template类型。
部分特化的定义与通用模板的定义完全不会冲突。部分特化可以具有与通用类模板完全不同的成员集合。类模板成员的通用定义永远不会用来实例化类模板部分特化的成员。
C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]