模板

Header(头文件)中的防卫式声明、布局

// complex.h// guard 防卫式声明
#ifndef __COMPLEX__
#define __COMPLEX__// 0、forward declarations (前置声明)
#include <cmath>class ostream;
class complex;complex&__doapl (complex* ths, const complex& r);// 1、class declarations (类-声明)
class complex
{...
};// 2、class definition (类-定义)
complex::function ...#endif
// complex-test.cpp#include <iostream>
#incldue "complex.h" // *****HERE*****
using namespace std;int main() {...
}

class - 声明

class complex // class head
{ // class body
public:complex (double r = 0, double i = 0) : re (r), im (i) {  }// 有些函数在此直接定义,另一些在body之外定义complex& operator += (const complex&) ;double real () const { return re; }double imag () const { return im; }
private:double re, im;friend complex& __doapl (complex*, const complex&);};// .cpp
{complex c1(2,1);complex c2;
}

类模板

template<typename T>
class complex // class head
{ // class body
public:complex (T r = 0, Ti = 0) : re (r), im (i) {  }// 有些函数在此直接定义,另一些在body之外定义complex& operator += (const complex&) ;T real () const { return re; }T imag () const { return im; }
private:T re, im;friend complex& __doapl (complex*, const complex&);};
// .cpp
{complex<double> c1(2.5,1.5);complex<int> c2(2,6);
}

函数模板

stone r1(2, 3), r2(3, 3), r3;
r3 = min(r1, r2);
template <class T>
inline
const T& min(const T& a, const T& b)
{return b < a ? b : a;
}
class stone
{public:stone(int w, int h, int we): _w(w), _h(h), _weight(we){  }bool operator<const stone& rhs) const{return _weight < rhs. _weight;}
private:int _w, _h, _weight;
}

操作符重载

头文件

#ifndef __COMPLEX__ // 1.防卫式声明
#define __COMPLEX__ #include <iostream>
using std::ostream;// 2.class head
class complex
{ // class body
public:// 3.构造函数 inlinecomplex (double r = 0, double i = 0) // pass by 什么都可以,因为double是4个字节: re(r), im(i) // 设初值{  }// 有些函数在此直接定义,另一些在body之外定义complex& operator += (const complex&); // 5.重载+=,设计为成员函数// 4.取得实部和虚部,不会改data,所以函数是const;inlinedouble real() const { return re; } double imag() const { return im; }
private:double re, im;// 6.do assignment-plus 函数中想直接取得re和im,所以声明成友元函数friend complex& __doapl(complex*, const complex&);
};#endif // __COMPLEX__

cpp文件

#include "complex.h"// 重载+=
// 想法类似下面
inline complex&
__doapl(complex* ths, const complex& r) {ths->re += r.re;ths->im += r.im;return *ths;
}// +=左边是默认的this指针;
// 右边最好引用&,不改变右边,所以const;
// 返回值不是局部变量,可以传引用&;
// 希望成为inline函数
inline complex&
complex::operator += (const complex& r) {return __doapl(this, r);
}// 重载 +,非成员函数:因为不一定就是复数加复数,可以实数加复数
// 因为不能加到x和y上,所以会加到一个新的局部变量上,所以return by value
inline complex
operator + (const complex& x, const complex& y) {return complex(x.real() + y.real(), x.imag() + y.imag());
}inline complex
operator + (double x, const complex& y) {return complex(x + y.real(), y.imag());
}inline complex
operator + (const complex& x, double y) {return complex(x.real() + y, x.imag());
}// 重载 <<
// 因为不可以写成 c1 << cout,所以要写成非成员函数
// os传进来以后,每次输出一点东西,就会改变,所以是&
// 返回ostream 是为了可以连续输出;ostream 不是局部变量,可以返回引用
// 为了使用ostream,要 #include <iostream>
inline ostream&
operator << (ostream& os, const complex& x) {return os << "(" << x.real() << "," << x.imag() << ")";
}

模板特化

概述

模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

函数模板特化

函数模板特化指函数模板在模板参数为特定类型下的特定实现。查看以下示例:

#include <iostream>
using namespace std;template<typename T> T Max(T t1,T t2) {return (t1>t2)?t1:t2;
}typedef const char* CCP;
template<> CCP Max<CCP>(CCP s1,CCP s2) {return (strcmp(s1,s2)>0)?s1:s2;
}int main() {// 隐式调用实例:int Max<int>(int,int)int i=Max(10,5);// 显式调用特化版本:const char* Max<const char*>(const char*,const char*)const char* p=Max<const char*>("very","good");cout<<"i:"<<i<<endl;cout<<"p:"<<p<<endl;}

程序正常编译运行结果:

i:10
p:very

在函数模板显示特化定义(Explicit Specialization Definition)中,显示关键字 template 和一对尖括号 <>,然后是函数模板特化的定义。该定义指出了模板名、被用来特化模板的模板实参,以及函数参数表和函数体。在上面的程序中,如果不给出函数模板Max< T>在T为const char*时的特化版本,那么在比较两个字符串的大小时,比较的是字符串的起始地址的大小,而不是字符串的内容在字典序中的先后次序。

除了定义函数模板特化版本外,还可以直接给出模板函数在特定类型下的重载形式(普通函数)。使用函数重载可以实现函数模板特化的功能,也可以避免函数模板的特定实例的失效。例如,把上面的模板特化可以改成如下重载函数。

typedef const char* CCP;
CCP Max(CCP s1,CCP s2) {return (strcmp(s1,s2)>0)?s1:s2;
}

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面:

(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。

(2)如果使用普通重载函数,那么在分离编译模式下,需要在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。

类模板特化

类模板特化类似于函数模板的特化,即类模板参数在某种特定类型下的具体实现。考察如下代码:

#include <iostream>
using namespace std;template<typename T>class A {T num;
public:A(){num=T(6.6);}void print(){cout<<"A'num:"<<num<<endl;}
};template<> class A<char*> {char* str;
public:A() {str="A' special definition ";}void print() {cout<<str<<endl;}
};int main() {A<int> a1;      //显示模板实参的隐式实例化a1.print();A<char*> a2;    //使用特化的类模板A2.print();
}

程序输出结果如下:

A'num:6
A' special definition

模板偏特化

概述

模板偏特化(Template Partitial Specialization)是模板特化的一种特殊情况,指显示指定部分模板参数而非全部模板参数,或者指定模板参数的部分特性分而非全部特性,也称为模板部分特化。与模板偏特化相对的是模板全特化,指对所有模板参数进行特化。模板全特化与模板偏特化共同组成模板特化。

模板偏特化主要分为两种,一种是指对部分模板参数进行全特化,另一种是对模板参数特性进行特化,包括将模板参数特化为指针、引用或是另外一个模板类。

函数模板偏特化

假如我们有一个 compare 函数模板,在比较数值大小时没有问题,如果传入的是数值的地址,我们需要比较两个数值的大小,而非比较传入的地址大小。此时我们需要对 compare 函数模板进行偏特化。考察如下代码:

#include <vector>
#include <iostream>
using namespace std;// 函数模板
template<typename T, class N> void compare(T num1, N num2) {cout << "standard function template" << endl;if(num1>num2) {cout << "num1:" << num1 << " > num2:" << num2 <<endl;} else {cout << "num1:" << num1 << " <= num2:" << num2 << endl;}
}// 对部分模板参数进行特化
template<class N> void compare(int num1, N num2) {cout<< "partitial specialization" <<endl;if (num1>num2)cout << "num1:" << num1 << " > num2:" << num2 << endl;elsecout << "num1:" << num1 << " <= num2:" << num2 << endl;
}// 将模板参数特化为指针(模板参数的部分特性)
template<typename T, class N> void compare(T* num1, N* num2) {cout << "new partitial specialization" << endl;if (*num1>*num2)cout << "num1:" << *num1 << " > num2:" << *num2 << endl;elsecout << "num1:" << *num1 << " <= num2:" << *num2 << endl;
}// 将模板参数特化为另一个模板类
template<typename T, class N> void compare(std::vector<T>& vecLeft, std::vector<T>& vecRight) {cout << "to vector partitial specialization" << endl;if (vecLeft.size()>vecRight.size())cout << "vecLeft.size()" << vecLeft.size() << " > vecRight.size():" << vecRight.size() << endl;elsecout << "vecLeft.size()" << vecLeft.size() << " <= vecRight.size():" << vecRight.size() << endl;
}int main() {// 调用非特化版本 compare<int,int>(int num1, int num2)compare<int,int>(30,31);// 调用偏特化版本 compare<char>(int num1, char num2)compare(30,'1');int a = 30;char c = '1';// 调用偏特化版本 compare<int,char>(int* num1, char* num2)compare(&a, &c);vector<int> vecLeft{0};vector<int> vecRight{1,2,3};// 调用偏特化版本 compare<int,char>(int* num1, char* num2)compare<int,int>(vecLeft,vecRight);}

程序输出结果如下:

standard function template
num1:30 <= num2:31
partitial specialization
num1:30 <= num2:1
new partitial specialization
num1:30 <= num2:1
to vector partitial specialization
vecLeft.size()1 <= vecRight.size():3

类模板偏特化

类模板的偏特化与函数模板的偏特化类似。考察如下代码:

#include <vector>
#include <iostream>
using namespace std;// 类模板
template<typename T, class N> class TestClass {public:static bool comp(T num1, N num2) {cout <<"standard class template"<< endl;return (num1<num2) ? true : false;}
};// 对部分模板参数进行特化
template<class N> class TestClass<int, N> {public:static bool comp(int num1, N num2) {cout << "partitial specialization" << endl;return (num1<num2) ? true : false;}
};// 将模板参数特化为指针
template<typename T, class N> class TestClass<T*, N*> {public:static bool comp(T* num1, N* num2) {cout << "new partitial specialization" << endl;return (*num1<*num2) ? true : false;}
};// 将模板参数特化为另一个模板类
template<typename T, class N> class TestClass<vector<T>,vector<N>> {public:static bool comp(const vector<T>& vecLeft, const vector<N>& vecRight) {cout << "to vector partitial specialization" << endl;return (vecLeft.size()<vecRight.size()) ? true : false;}
};int main() {// 调用非特化版本cout << TestClass<char, char>::comp('0', '1') << endl;    // 调用部分模板参数特化版本cout << TestClass<int,char>::comp(30, '1') << endl;      // 调用模板参数特化为指针版本int a = 30;char c = '1';cout << TestClass<int*, char*>::comp(&a, &c) << endl;     // 调用模板参数特化为另一个模板类版本vector<int> vecLeft{0};vector<int> vecRight{1,2,3};cout << TestClass<vector<int>, vector<int>>::comp(vecLeft,vecRight) << endl;   }

程序输出结果:

standard class template
1
partitial specialization
1
new partitial specialization
1
to vector partitial specialization
1

参考
[1]侯捷c++
[2]C++ 模板特化与偏特化

操作符重载and模板(泛化, 全特化, 偏特化)相关推荐

  1. 模板 泛化 全特化 偏特化

    template<>出现,就表示要特化了 为什么要特化呢?泛化不就是为了解决数据类型不一致吗? 泛化是通用,特化是优化,缺一不可 特化,又被成为全特化 full-specializatio ...

  2. (函数/类模板)的(偏特化/全特化)

    特化的概念 特化,就是将泛型的东东搞得"具体化"一些,从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,或受到特定的修饰或完全被指定了下 ...

  3. 语言 排序后重置索引_当C++操作符重载、模板遇到排序(一)

    本文主要是使用C++的操作符重载和模板的语法,完成一些结构性数据的排序,本文会实现冒泡.插入和选择三种简单排序,后续会更新希尔排序.快速排序.归并排序三种高级简单排序的方法. 1.C++操作符重载 操 ...

  4. 第十天2017/04/21(2、泛型编程:模板 / 全特化、偏特化)

    1.什么是模板? template<class T1,class T2,.....> 类属----类型参数化,又称参数模板使得程序可以从逻辑功能上抽象,把被处理的对象(数据)的类型作为参数 ...

  5. C++模板的全特化和偏特化

    C++模板的全特化与偏特化 全特化 偏特化 例子 总结 全特化 全特化一般用于处理有特殊要求的类或者函数,此时依靠泛型模板无法处理这种情况.,因此全特化可以运用在类模板和函数模板当中.其模板参数列表为 ...

  6. C++ 模板 全特化与偏特化

    C++ 模板 全特化与偏特化 模板 模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性.模版可以分为两类,一个是函数模版,另外一个是 ...

  7. C++——模板特化和偏特化

    1.引言 C++中的模板分为类模板和函数模板,虽然它引进到C++标准中的时间不是很长,但是却得到了广泛的应用,这一点在STL中有着充分的体现.目前,STL在C++社区中得到了广泛的关注.应用和研究.理 ...

  8. 【C++ Primer | 16】容器适配器全特化、偏特化

    上面对模板的特化进行了总结.那模板的偏特化呢?所谓的偏特化是指提供另一份模板定义式,而其本身仍为模板:也就是说,针对模板参数更进一步的条件限制所设计出来的一个特化版本.这种偏特化的应用在STL中是随处 ...

  9. C++模板特化和偏特化(二)

    一.函数模板 (1)函数的匹配优先级: 普通函数: 重载函数: 普通函数模板: 全特化函数模板. 函数模板不允许使用偏特化,如有需求,改成模板函数的重载. (2)函数模板特化 函数模板特化主要的用途都 ...

最新文章

  1. android 获取短信验证码倒计时
  2. 暑假周总结七8.26
  3. Linux 中 SVN 重启关闭
  4. struts2 - ation 访问 Servlet api
  5. webservice3
  6. (转)VmWare下安装CentOS6图文安装教程
  7. QQ总显示服务器请求中,网站添加QQ登陆 报错 可能是服务器无法请求https协议 解决方法...
  8. H3C IPV6实验
  9. 写出一手烂代码的 19 条准则!
  10. renpy 如何执行2个action_可执行的网络推广方案如何策划 8个维度 学会了策划方案不求人...
  11. 自抗扰控制理论(一)ADRC的原理
  12. 通过R访问世界银行数据(World Bank Data)分析经济
  13. ubuntu16.04安装QQ完整版(附网盘资源)
  14. qt webview 忽略 ssl错误 ignoreSslErrors
  15. 大数据必学语言Scala(三十一):scala面向对象 特质(trait)
  16. 【原创】从头开始,使用安卓系统WebView做一个功能强大的Epub阅读器(五)
  17. midi java_Java程序中添加播放MIDI音乐功能的实现方法详解
  18. 学历低,无法胜任工作,大佬告诉你应该怎么做
  19. 安装了java但是系统显示没安装_我的世界 已经安装了java,但还是显示没有安装,怎么办...
  20. html鼠标滑过图片透明,鼠标滑过图片透明度发生改变的特效 - YangJunwei

热门文章

  1. 使用Arduino ESP32 通过PWM波控制大疆GM6020以及3508无刷电机(更新)
  2. android调起QQ聊天,QQ个人资料,QQ群资料
  3. 马氏距离 vs 欧氏距离
  4. ArcGIS教程:了解欧氏距离分析
  5. 【Python画图 04】标注
  6. JAVA实现获取指定日期所在的周的所有日期
  7. 从计算云到云计算,云可造化万象
  8. debian、ubuntu:使用apt包管理器可能存在的问题! 让新手望而却步!
  9. R语言plotly可视化:plotly可视化回归面(plane)、使用mesh3d和add_surface实现三维回归曲面
  10. c语言李白喝酒答案,李白嗜酒文言文答案