函数模板的特化

转自https://blog.csdn.net/ljq32/article/details/7916723

模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,
当以特化定义时的形参使用模板时,将调用特化版本,模板特化分为全特化和偏特化;
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). 数组或函数指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规转换,数组转换为指向实参第一个元素的指针,函数实参当做指向函数类型的指针;注意:函数模板中,模板形参表中的非类型形参遵循常规转换原则。
 
--------------------- 
作者:种菜的 
来源:CSDN 
原文:https://blog.csdn.net/ljq32/article/details/7916723 
版权声明:本文为博主原创文章,转载请附上博文链接!

#include <iostream>
#include <cstring>

using namespace std;

//泛型版本
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);
}

int main()
{
    {
      cout << compare(1, 2) << endl;  //根据实参类型进行实参推断,将为该调用实例化int compare(int, int)
    }
    {
      char a[] = {"abc"}; //一个普通字符数组,不是指针,形参为引用时,数组大小成为形参的一部分,数组不转换为指针,类型不匹配;
      const char b[] = {"abc"}; //一个常量字符数组,不是指针,类型不匹配;
      std::cout << compare(a, b) << std::endl;
    }
    {
      char *pc0 = "ddd"; //一个非const指针,指向非const数据,但是特化版本的形参类型是一个指向const数据的const引用,强调了指针指向的数据类型是const,
                       //也就是说实参指针指向的数据类型必须是const即指针存储的数据必须是const的,但这里不是因此类型不匹配;
      char * const pc1 = "ddd"; //一个const指针,指向非const数据,类型不匹配,原因同上,和指针是否是const没关系,和指针存储的数据类型有关;
      std::cout << compare(pc0, pc1) << std::endl;
      const char * const pc2 = "ddd"; //一个const指针,指向const数据,满足特化版本的形参(一指向const数据的const引用),类型匹配;
      const char * pc3 = "ddd"; //一个非const指针,指向const数据,类型匹配,原因同上;
      std::cout << compare(pc2, pc3) << std::endl;
    }
    {
      //为实参类型 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 *,所以必须提供两个特化版本。
 }

猜你喜欢

转载自blog.csdn.net/janeqi1987/article/details/87438607