模板函数的特化

  1. 模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,
  2. 当以特化定义时的形参使用模板时,将调用特化版本,模板特化分为全特化和偏特化;
  3. 1. 函数模板的特化,只能全特化;
  4. //泛型版本
  5. template <class T> int compare(const T &v1, const T &v2)
  6. {
  7. if(v1 < v2) return -1;
  8. if(v2 > v1) return 1;
  9. return 0;
  10. }
  11. 对于该函数模板,当实参为两个char指针时,比较的是指针的大小,而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本:
  12. //为实参类型 const char * 提供特化版本
  13. template <> int compare<const char *>(const char * const &v1, const char * const &v2)
  14. {
  15. return strcmp(v1, v2);
  16. }
  17. a: template <> //空模板形参表
  18. b: compare<const char *> //模板名字后指定特化时的模板形参即const char *类型,就是说在以实参类型 const char * 调用函数时,将产生该模板的特化版本,而不是泛型版本,也可以为其他指针类型定义特化版本如int *.
  19. 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,否则类型不匹配;
  20. //特化版本 (int *)
  21. template <> int compare<const int *>(const int * const &v1, const int * const &v2)//v1 和 v2 是指向const 整形变量的const引用;
  22. {
  23. if(*v1 < *v2) return -1;//像指针一样操作,可以理解v1,v2就是指针,因为它是指针的引用;
  24. if(*v2 > *v1) return 1;
  25. }
  26. 2. 与其他函数声明一样,应该在一个头文件中包含模板特化的声明,在使用特化模板的源文件中包含该头文件;
  27. 注意,函数模板调用时的实参与模板形参不进行常规转换,特化与泛型版本都不进行常规转换,类型必须完全一致,非函数模板在实参调用时进行常规转换;普通函数和函数模板调用时的实参与模板形参都进行两种转换:
  28. (1). const转换:接受const引用或者const指针的函数,可以分别以非const对象的引用或者指针来调用,无需产生新实例,如果函数接受非引用类型或者非指针类型,形参类型和实参类型忽略const,无论传递const还是非const对象给非引用类型的函数,都使用相同的实例;
  29. (2). 数组或函数指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规转换,数组转换为指向实参第一个元素的指针,函数实参当做指向函数类型的指针;注意:函数模板中,模板形参表中的非类型形参遵循常规转换原则。
  30. //例子:
  31. //16.6.1.h
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <iostream>
  35. //泛型版本
  36. template <typename T> int compare(const T &v1, const T &v2)
  37. {
  38. std::cout << "template <typename T>" << std::endl;
  39. if(v1 < v2) return -1;
  40. if(v2 < v1) return 1;
  41. return 0;
  42. }
  43. //为实参类型 const char * 提供特化版本
  44. //template <> int compare(const char * const &v1, const char * const &v2) //省略了函数名后边的显示模板实参,因为可以从函数形参表推断出来,本定义与下边的定义都是正确的;
  45. template <> int compare<const char *>(const char * const &v1, const char * const &v2)
  46. {
  47. std::cout << "template <> int compare<const char *>" << std::endl;
  48. return strcmp(v1, v2);
  49. }
  50. //为实参类型 char * 提供特化版本
  51. //template <> int compare(char * const &v1, char * const &v2)
  52. template <> int compare<char *>(char * const &v1, char * const &v2)
  53. {
  54. std::cout << "template <> int compare<char *>" << std::endl;
  55. return strcmp(v1, v2);
  56. }
  57. //16.6.1.cpp
  58. #include <iostream>
  59. #include "16.6.1.h"
  60. using namespace std;
  61. int main()
  62. {
  63. cout << compare(1, 2) << endl;  //根据实参类型进行实参推断,将为该调用实例化int compare(int, int)
  64. char a[] = {"abc"}; //一个普通字符数组,不是指针,形参为引用时,数组大小成为形参的一部分,数组不转换为指针,类型不匹配;
  65. const char b[] = {"abc"}; //一个常量字符数组,不是指针,类型不匹配;
  66. char *p = "ddd"; //一个非const指针,指向非const数据,但是特化版本的形参类型是一个指向const数据的const引用,强调了指针指向的数据类型是const,也就是说实参指针指向的数据类型必须是const即指针存储的数据必须是const的,但这里不是因此类型不匹配;
  67. char * const pc = "ddd"; //一个const指针,指向非const数据,类型不匹配,原因同上,和指针是否是const没关系,和指针存储的数据类型有关;
  68. const char * const pc = "ddd"; //一个const指针,指向const数据,满足特化版本的形参(一指向const数据的const引用),类型匹配;
  69. const char * pc = "ddd"; //一个非const指针,指向const数据,类型匹配,原因同上;
  70. //为实参类型 const char * 提供特化版本
  71. const char *pa = "abc"; // 或者 const char * const pa = "abc"; 与指针指向数据类型是const还是非const有关,而与指针是const还是非const没关系,因为,特化版本的形参类型是一个指向指针的cosnt引用,因此不会改变指针的值,所以指针本身是const还是非cosnt没有关系,但是由于特化版本形参的引用指向的指针所指向的数据类型是const,所以不能使用一个指向非const数据的指针调用特化版本,因为数据类型不匹配;
  72. const char *pb = "bbd";
  73. 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,类型必须完全匹配;
  74. //为实参类型 char * 提供特化版本
  75. char *pc = "ccc";
  76. char *pd = "ddd";
  77. cout << compare(pc, pd) << endl;
  78. return 0;
  79. //char * 与 const char * 是两个不同的数据类型(前者存储的数据是常量与后者存储的数据是非常量),虽然可以将类型 char * 通过常规转换,转换成 const char *,但是作为模板实参,在模板实参推断时,不会把函数调用时的实参类型 char * 转换为模板形参类型const char *,所以必须提供两个特化版本。
  80. }
  81. 运行结果:
  82. template <typename T>
  83. -1
  84. template <> int compare<const char *>
  85. -1
  86. template <> int compare<char *>
  87. -1

模板函数的特化

时间: 2024-10-10 03:51:28

模板函数的特化的相关文章

C++普通函数与模板函数以及特化函数重载的优先级问题

在面对C++模板的时候,需要十分注意,因为模板的复杂性有很多情况,所以最好学习模板的方法我个人认为就是用到就去学,用不到就尽量别去看各种奇门怪技,因为你就算看了,好不容易搞懂模板的实现内部了,包括元编程啊什么的,但真正用到工作中的我相信很少,不久你也会忘掉,所以,对于模板,我们可以采取用到哪学到哪的观念去学习,这样可以节省时间并且让效率最大化. 今天主要讲在关于模板特化已经函数重载的问题,简单举下例子 1 void say(int value); 2 template <typename T>

模板函数 重载/特化

?见代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

函数模板示例(三) 模板函数的特化与重载

function3.h中的代码: #ifndef FUNCTION3_H #define FUNCTION3_H #include <string> #include <cstring> #include <iostream> template <typename T> T myMax(const T p1, const T p2) { std::cout << "template default func" <<

C++ 模板函数 知多少?

要讲模板函数,我们先来看个例子,假如老板要让你写一个排序函数,假定最开始用户的要求可能只是 int .那么你花了大把功夫写了一段代码: void Sort(int* begin, int* end, bool(*cmp)(int, int)) //第三个参数是优先级函数,在这里我就简单用的冒泡排序.如果不会排序的请不要深究排序实现过程. { int* i, *j; for (i = begin; i != end; i++) { int* max = i; for (j = i + 1; j !

函数模板与模板函数及模板类与模板的特化

函数模板( Function templates) * 模板(Templates)使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载.这在一定程度上实现了宏(macro)的作用.它们的原型定义可以是下面两种中的任何一个: template <class identifier> function_declaration; template <typename identifier> function_decla

忍不住吐槽类模板、模板类、函数模板、模板函数

最近在查资料,发现了一些blog上写"类模板.模板类.函数模板.模板函数的区别"之类的文章.一看之下,闭起眼睛想想,自己写了这么久C++,知道模板,知道函数,也知道类.如果单独问我,类模板或者模板类,我都认为是采用了模板的类.但不知道这"类模板.模板类.函数模板.模板函数"是什么东西. 仔细看了下文章,忍不住吐槽了.其实就是采用模板的类叫类模板,实例化后叫模板类.采用模板的函数叫函数模板,实例化后叫模板函数.好吧,你赢了.知道模板的都会知道,模板实例化后会由编译器生

C++学习之模板 ----函数模板、类模板

本博文主要讨论函数模板与类模板以及其简单应用. 1).作用:函数模板和类模板都可以看做是一种代码产生器,往里面放入具体的类型,得到具体化的函数或者class. 2).编译(分为两步): a):实例化之前,先检查模板本身语法是否正确: b):根据 函数调用或者类模板调用 ,先去实例化模板代码,产生具体的函数/类. 也就是说, 没有函数调用或者类类型对象声明,就不会实例化模板代码,在目标文件obj中找不到模板的痕迹. 3):优缺点 模板的缺点是代码膨胀,编译速度慢,而优点是运行速度快. 一.函数模板

模板的全特化与偏特化

模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的. 模板分为类模板与函数模板,特化分为全特化与偏特化.全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分. 先看类模板: [cpp] view plain copy template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<&quo

聊聊C++模板函数与非模板函数的重载

函数重载在C++中是一个很重要的特性.之所以有了它才有了操作符重载.iostream.函数子.函数适配器.智能指针等非常有用的东西. 平常在实际的应用中多半要么是模板函数与模板函数重载,或者是非模板函数与非模板重载.而让模板函数与非模板函数重载的情况却很少. 前几天在项目中偶然遇到了一个模板函数与非模板函数重载的诡异问题,大概相当于下面这种情况: 1 template <typename T> 2 int compare(const T& lhs, const T& rhs)