函数模板的特化

来源:互联网 发布:杭州二更网络二次融资 编辑:程序博客网 时间:2024/06/03 01:44
模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,当以特化定义时的形参使用模板时,将调用特化版本,模板特化分为全特化和偏特化;1. 函数模板的特化,只能全特化;//泛型版本template <class T> int compare(const T &v1, const T &v2){  if(v1 < v2) return -1;  if(v2 > v1) return 1;  return 0;}对于该函数模板,当实参为两个char指针时,比较的是指针的大小,而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本://为实参类型 const char * 提供特化版本template <> int compare<const char *>(const char * const &v1, const char * const &v2){  return strcmp(v1, v2);}  a: template <> //空模板形参表  b: compare<const char *> //模板名字后指定特化时的模板形参即const char *类型,就是说在以实参类型 const char * 调用函数时,将产生该模板的特化版本,而不是泛型版本,也可以为其他指针类型定义特化版本如int *.  c: (const char * const &v1, const char * const &v2)//可以理解为: const char * const &v1, 去掉const修饰符,实际类型是:char *&v1,也就是v1是一个引用,一个指向char型指针的引用,即指针的引用,加上const修饰符,v1就是一个指向const char 型指针的 const引用,对v1的操作就是对指针本身的操作,操作方式与指针一致,比如*v1,是正确的;//注意这里的const char *, 由于形参是一个指向指针的const引用,所以调用特化版本时的实参指针类型(并非存储的数据的类型)可以为const也可以为非const,但是由于这里形参指针指向的数据类型为const char *(强调存储的数据是const),所以实参指针所指向的数据类型也必须为const,否则类型不匹配;  //特化版本 (int *)template <> int compare<const int *>(const int * const &v1, const int * const &v2)//v1 和 v2 是指向const 整形变量的const引用;{  if(*v1 < *v2) return -1;//像指针一样操作,可以理解v1,v2就是指针,因为它是指针的引用;  if(*v2 > *v1) return 1;} 2. 与其他函数声明一样,应该在一个头文件中包含模板特化的声明,在使用特化模板的源文件中包含该头文件; 注意,函数模板调用时的实参与模板形参不进行常规转换,特化与泛型版本都不进行常规转换,类型必须完全一致,非函数模板在实参调用时进行常规转换;普通函数和函数模板调用时的实参与模板形参都进行两种转换: (1). const转换:接受const引用或者const指针的函数,可以分别以非const对象的引用或者指针来调用,无需产生新实例,如果函数接受非引用类型或者非指针类型,形参类型和实参类型忽略const,无论传递const还是非const对象给非引用类型的函数,都使用相同的实例; (2). 数组或函数指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规转换,数组转换为指向实参第一个元素的指针,函数实参当做指向函数类型的指针;注意:函数模板中,模板形参表中的非类型形参遵循常规转换原则。//例子://16.6.1.h#include <stdio.h>#include <string.h>#include <iostream>//泛型版本template <typename T> int compare(const T &v1, const T &v2){  std::cout << "template <typename T>" << std::endl;  if(v1 < v2) return -1;  if(v2 < v1) return 1;  return 0;}//为实参类型 const char * 提供特化版本//template <> int compare(const char * const &v1, const char * const &v2) //省略了函数名后边的显示模板实参,因为可以从函数形参表推断出来,本定义与下边的定义都是正确的;template <> int compare<const char *>(const char * const &v1, const char * const &v2){  std::cout << "template <> int compare<const char *>" << std::endl;  return strcmp(v1, v2);}//为实参类型 char * 提供特化版本//template <> int compare(char * const &v1, char * const &v2)template <> int compare<char *>(char * const &v1, char * const &v2){  std::cout << "template <> int compare<char *>" << std::endl;  return strcmp(v1, v2);}//16.6.1.cpp#include <iostream>#include "16.6.1.h"using namespace std;int main(){  cout << compare(1, 2) << endl;  //根据实参类型进行实参推断,将为该调用实例化int compare(int, int)  char a[] = {"abc"}; //一个普通字符数组,不是指针,形参为引用时,数组大小成为形参的一部分,数组不转换为指针,类型不匹配;  const char b[] = {"abc"}; //一个常量字符数组,不是指针,类型不匹配;  char *p = "ddd"; //一个非const指针,指向非const数据,但是特化版本的形参类型是一个指向const数据的const引用,强调了指针指向的数据类型是const,也就是说实参指针指向的数据类型必须是const即指针存储的数据必须是const的,但这里不是因此类型不匹配;  char * const pc = "ddd"; //一个const指针,指向非const数据,类型不匹配,原因同上,和指针是否是const没关系,和指针存储的数据类型有关;  const char * const pc = "ddd"; //一个const指针,指向const数据,满足特化版本的形参(一指向const数据的const引用),类型匹配;  const char * pc = "ddd"; //一个非const指针,指向const数据,类型匹配,原因同上;  //为实参类型 const char * 提供特化版本  const char *pa = "abc"; // 或者 const char * const pa = "abc"; 与指针指向数据类型是const还是非const有关,而与指针是const还是非const没关系,因为,特化版本的形参类型是一个指向指针的cosnt引用,因此不会改变指针的值,所以指针本身是const还是非cosnt没有关系,但是由于特化版本形参的引用指向的指针所指向的数据类型是const,所以不能使用一个指向非const数据的指针调用特化版本,因为数据类型不匹配;  const char *pb = "bbd";  cout << compare(pa, pb) << endl; // 根据实参类型为该调用实例化特化版本int compare(const * const &v1, const * const &v2), 函数模板调用时的实参与模板形参不进行常规转换;由于编译器对特化版本不进行实参与形参的常规转换,所以调用的实参类型必须与特化版本的声明完全一致,否则将从泛型版本进行实例化,或者函数匹配错误;由于compare声明的形参都是const char *即char *型指针存储的是const数据,所以不能传递一个存储了非const数据的char *型指针(尽管此时的cosnt char *形参不会改变实参指针指向的值),也不能传递一个const数组名字(此时数组名不会转换为指针),必须传递一个指向const数据的指针,即代码中的 const char *pa,类型必须完全匹配;  //为实参类型 char * 提供特化版本  char *pc = "ccc";  char *pd = "ddd";                                                                             cout << compare(pc, pd) << endl;  return 0;  //char * 与 const char * 是两个不同的数据类型(前者存储的数据是常量与后者存储的数据是非常量),虽然可以将类型 char * 通过常规转换,转换成 const char *,但是作为模板实参,在模板实参推断时,不会把函数调用时的实参类型 char * 转换为模板形参类型const char *,所以必须提供两个特化版本。 }  运行结果:  template <typename T>  -1  template <> int compare<const char *>  -1  template <> int compare<char *>  -1 


原创粉丝点击