文章目录

  • 一.泛型编程
  • 二.函数模板
    • (1).函数模板的实例化
    • (2).非类型模板参数
  • 三.类模板
    • (1).类模板的成员函数
    • (2).非类型模板参数
  • 四.模板的特化
    • (1).函数模板特化
    • (2).类模板特化
  • 五.模板不支持分离编译

一.泛型编程

在引入泛型编程之前,我们先来看这样一个问题,怎么做到实现一个通用的交换函数呢?

在C语言阶段我们可能会像下面这样写,需要分别实现不同类型的交换函数,又由于C语言不允许出现同名函数,所以函数名也需要不一样,写起来很繁琐

void Swapi(int* e1, int* e2)
{int tmp = *e1;*e1 = *e2;*e2 = tmp;
}
void Swapd(double* e1, double* e2)
{double tmp = *e1;*e1 = *e2;*e2 = tmp;
}

C++因为名称修饰规则支持了重载函数,虽然函数名可以一样,但还是要分别实现不同类型的交换函数,也很繁琐

void Swap(int& e1, int& e2)
{int tmp = e1;e1 = e2;e2 = tmp;
}
void Swap(double& e1, double& e2)
{double tmp = e1;e1 = e2;e2 = tmp;
}

我们也可以看出上述代码只是参数类型不一样,其他的实现过程和逻辑都是一样的,为了解决(每一个类型都要自己去实现一个函数出来)这样的繁琐的问题,我们引入了泛型编程

(1).所谓泛型编程,是以独立于任何特定类型的方式编写代码,使用泛型编程时,我们需要提供具体程序实例所操作的类或值

(2).模板是泛型编程的基础,模板是创建类或函数的蓝图或公式,我们给这些蓝图或公式足够的信息,让这些蓝图或公式真正的转变为具体的类或函数,这种转变发生在编译时

(3).模板支持将类型作为参数的程序设计方式,从而实现了对泛型程序设计的支持,也就是说C++模板机制允许将类型作为参数

二.函数模板

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

函数模板格式

template<typename T1, typename T2,......,typename Tn>
// 返回值类型 函数名(参数列表){}

上面的问题就得以解决了

template<class T>
void Swap(T& a, T& b)
{T tmp = a;a = b;b = tmp;
}

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事交给了编译器

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用 int 类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码

(1).函数模板的实例化

(1).隐式实例化 : 让编译器自己根据实参的类型推导模板参数的类型

template<class T>
T Add(const T& a, const T& b)
{return a + b;
}
int main()
{int a = 1, b = 2;cout << Add(a,b) << endl;
}

(2).显示实例化 : 在函数名后的<>中指定模板参数的实际类型

template<class T>
T Add(const T& a, const T& b)
{return a + b;
}
int main()
{int a = 1;double b = 2.2;cout<<Add<int>(a,b)<<endl;cout<<Add<double>(a,b)<<endl;
}

模板参数的匹配原则

(1). 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

(2). 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板实例化出来的函数

// 非模板函数
int Add(int left, int right)
{return left + right;
}
// 模板函数
template<class T>
T Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非模板函数匹配,编译器不需要进行模板实例化Add<int>(1, 2); // 调用编译器进行模板实例化的函数Add(1,2.0) // 调用编译器进行模板实例化的函数
}

(2).非类型模板参数

在模板参数列表里,还可以定义非类型参数,非类型参数代表的是一个值,既然非类型参数代表一个值,不是一个类型,肯定不能用typename/class关键字来修饰这个值,我们当然要用我们以往学习过的传统类型名来指定非类型参数了

当模板被实例化时,这种非类型模板参数的值,或者是用户提供的,或者是编译器自己推断的,但这些值必须都得是常量表达式,因为模板实例化发生在编译阶段

非类型模板参数的一些限制 :
(1). 浮点数、类对象以及字符串是不允许作为非类型模板参数的(只能为整形)。
(2). 非类型的模板参数必须在编译期就能确认结果。

#include<iostream>
using namespace std;
template<int a,int b>
int add1()
{return a + b;
}
template<class T,int a,int b>
int add2(T c)
{return c + a + b;
}
template<unsigned L1,unsigned L2>
int charscmp(const char(&p1)[L1],const char(&p2)[L2])
{return strcmp(p1, p2);
}
int main()
{cout << add1<1, 2>() << endl;cout << add2<int, 1, 2>(5) << endl;cout << add2<int, 1, 2>(1.6) << endl;cout << charscmp("test2", "test") << endl;
}

三.类模板

类模板定义格式 :

template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};

编译器不能为类模板推断模板类型参数,因此,类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

#include<iostream>
#include<vector>
using namespace std;
namespace lyp
{template<class T>class vector{public:vector():_a(nullptr),_size(0),_capacity(0){}~vector(){delete[]_a;_a = nullptr;_size = _capacity = 0;}T& operator[](size_t pos){return _a[pos];}int size(){return _size;}void push_back(const T& x){// 需要动态增容if (_size == _capacity){int newcapacity = _capacity == 0 ? 4 : _capacity * 2;T* tmp = new T[newcapacity];if (_a){memcpy(tmp, _a, sizeof(T) * _size);}delete[]_a;_a = tmp;_capacity = newcapacity;}_a[_size++] = x;}private:T* _a;int _size;int _capacity;};
}
int main()
{lyp::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (int i = 0; i < v1.size(); i++){// v1.operator[](3)cout << v1[i] << endl;}std::vector<double> v2;v2.push_back(1.1);v2.push_back(2.2);v2.push_back(3.3);v2.push_back(4.4);for (int i = 0; i < v2.size(); i++){cout << v2[i] << endl;}
}

(1).类模板的成员函数

(1).类模板成员函数,可以写在类模板定义中,这种写在类模板定义中的成员函数会被隐式声明成inline函数
(2).类模板一旦被实例化之后,那么这个模板的每个实例都会有自己版本的成员函数,所以,类模板的成员函数是有模板参数的,因此,如果要把类模板成员函数的定义写到类模板定义的外面,须以关键字template开始,后接模板参数列表,同时,在类模板名后用<>将模板参数列表里的所有模板参数名列出来
(3).一个类模板可能有多个成员函数,当实例化模板以后,后续如果没有使用某个成员函数,则该成员函数不会实例化

template<typename T>
class myvector
{public:// 构造函数myvector();// 赋值运算符重载myvector& operator=(const myvector& v);// 会被隐式声明成内联函数void func(){// .....}
};
template<class T>
myvector<T>& myvector<T>:: operator=(const myvector& v)
{// .....
}

(2).非类型模板参数

template<class T,int size = 10>
class myarray
{public:void func();
private:T arr[size];
};
template<class T,int size>
void myarray<T,size>::func()
{cout << size << endl;return;
}

四.模板的特化

模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本

#include<iostream>
using namespace std;
template<class T>
bool IsEqual(const T& left, const T& right)
{return left == right;
}
int main()
{const char* p1 = "hello";const char* p2 = "hello";cout << IsEqual(p1, p2) << endl;;return 0;
}

上面的模板在比较字符数组时并不会达到我们想要的效果,因为该模板比较的是字符数组的地址,比较的结果是不相等,但我们想要的结果是相等,所以需要模板特化

(1).函数模板特化

解决方案 :

#include<iostream>
using namespace std;
template<class T>
bool IsEqual(const T& left, const T& right)
{return left == right;
}
template<>
bool IsEqual<const char*>(const char* const& left, const char* const& right)
{return strcmp(left, right) == 0;
}
int main()
{const char* p1 = "hello";const char* p2 = "hello";cout << IsEqual(p1, p2) << endl;return 0;
}

(1). template <> : 空模板形参表

(2). compare<const char *> : 模板名字后指定特化时的模板形参即const char *类型,就是说在以实参类型 const char * 调用函数时,将产生该模板的特化版本,而不是泛型版本,也可以为其他指针类型定义特化版本如int *.

(3). (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,否则类型不匹配;

函数模板特化的例子

template<class T,class U>
void tfunc(T t, U u)
{cout << "泛化版本" << endl;cout << t << endl;cout << u << endl;
}
template<>
void tfunc<int, double>(int t, double u)
{cout << "全特化版本" << endl;cout << t << endl;cout << u << endl;
}
template<class U>
void tfunc(double t,U u)
{cout << "重载实现类似偏特化版本" << endl;cout << t << endl;cout << u << endl;
}

(2).类模板特化

(1) . 全特化

全特化即是将模板参数列表中所有的参数都确定化

#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{public:A(){cout << "A<T1,T2>" << endl;}
};
template<>
class A<int, int>
{public:A(){cout << "A<int,int>" << endl;}
};
int main()
{A<int, double> a;A<int, int> aa; // 全特化return 0;
}

(2). 偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本

1). 部分特化

#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{public:A(){cout << "A<T1,T2>" << endl;}
};
template<class T>
class A<T, int>
{public:A(){cout << "A<T,int>" << endl;}
};
int main()
{A<int,double> a;A<int, int> aa; // 偏特化return 0;
}

2). 参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版
本。

#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{public:A(){cout << "A<T1,T2>" << endl;}
};
template<class T1,class T2>
class A<T1*, T2*>
{public:A(){cout << "A<T1*,T2*>" << endl;}
};
template<class T1, class T2>
class A<T1&, T2&>
{public:A(){cout << "A<T1&,T2&>" << endl;}
};
int main()
{A<int,int> a;A<int*, double*> aa; // A<T1*,T2*>A<int&, double&> aaa;  // A<T1&,T2&>A<int*, double&> aaaa; // A<T1,T2>return 0;
}

五.模板不支持分离编译

在编译阶段,每一个cpp文件都是相对独立的,并不知道另一个编译文件的存在,若存在外部调用,会在链接阶段进行重定位。

模板的实例化其实只能发生在本编译单元的调用。如果出现非本编译单元的模板调用,也就是分离式编译,只能等待链接时重定位,但是模板并没有实例化,所以会出现链接出错。

因此,建议将声明和定义放到一个文件 “xxx.hpp” 里面或者"xxx.h"

// a.h
template<class T>
T Add(const T& a, const T& b);// a.cpp
template<class T>
T Add(const T& a, const T& b)
{return a + b;
}// main.cpp
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}

C++模板(函数模板/类模板)相关推荐

  1. C++函数模板和模板函数、类模板和模板类

    这期间有涉及到函数模板与模板函数,类模板与模板类的概念 (类似于类与类对象的区别) 注意:模板类的函数声明和实现必须都在头文件中完成,不能像普通类那样声明在.h文件中,实现在.cpp文件中. 1.函数 ...

  2. 泛函编程—模板函数_类模板

    函数业务逻辑一样,只是函数参数类型不同 函数模板的本质:类型参数化--泛型编程 语法: template <typename T> template <class T1,class ...

  3. 类模板,多种类型的类模板,自定义类模板,类模板的默认类型,数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器

     1.第一个最简单的类模板案例 #include "mainwindow.h" #include <QApplication> #include <QPush ...

  4. C++中模板类中的成员函数以及模板函数在类外定义

    在C++中,类中的成员函数可以在类外完成定义,从而显得类中的成员函数看起来简洁明了.但是模板类里的成员函数和模板函数与普通的成员函数在类外定义不同. 先定义一个模板类以及成员函数和模板函数: 接下我们 ...

  5. C++模板学习02(类模板)(类模板语法、类模板与函数模板的区别、类模板中的成员函数创建时机、类模板对象做函数参数、类模板与继承、类模板成员函数类外实现、类模板分文件编写、类模板与友元)

    C++引用详情(引用的基本语法,注意事项,做函数的参数以及引用的本质,常量引用) 函数高级C++(函数的默认参数,函数的占位参数,函数重载的基本语法以及注意事项) C++类和对象-封装(属性和行为作为 ...

  6. c++模板---3(类模板碰到继承问题,类模板类外实现,类模板与友元函数)

    类模板碰到继承问题 基类如果是模板类,必须让子类告诉编译器 基类中的T到底是什么类型 如果不告诉,那么无法分配内存,编译不过 利用参数列表class Child :public Base<int ...

  7. 模板 (函数模板语法 ,类模板与函数模板的区别,:函数模板案例,普通函数与函数模板的区别,普通函数与函数模板调用规则,模板的局限性,类模板分文件编写.cpp,Person.hpp,类模板与友元)

    **01:函数模板语法: #include<iostream> using namespace std;//交换两个整型函数 void swapInt(int &a ,int &a ...

  8. C++(11):模板函数的默认模板参数

    C++11支持在模板函数中使用默认的模板参数 #include <iostream> #include <typeinfo> using namespace std;templ ...

  9. 合同模板布局html,套打模板制作(合同类模板)

    在制作模板前,需要了解以下几点 ① 布局表格是默认每页出现的(设置布局表格是否每页出现是不生效的),而当布局表格本身的行设置了行高自适应而产生的换页,默认为当前布局表格页还未结束,因此,这种情况产生的 ...

  10. C++类模板(二)用类模板实现可变长数组

    #include <iostream> #include <cstring> using namespace std; template <class T> cla ...

最新文章

  1. Oracle9i数据库Data Guard实施及维护手册 1
  2. 二维数组最大关联子数组
  3. 笔记《javascript高级程序设计》 第12章 DOM2和DOM3
  4. 一次docker中的nginx进程响应慢问题定位记录
  5. VM虚拟机装Windows2000成功后VM Tools失败解决方法
  6. 关于android属性persistent的问题
  7. 深入理解安卓Activity
  8. java ee与j2ee_JavaEE/J2EE与J2SE/JDK版本的相关性
  9. 网络运维中静态路由 三层交换技术 动态路由 OSPF协议的配置
  10. 计算机中的云指的是什么,古语有云,古语有云中的云指的是什么
  11. 手机摄像头的等效焦距
  12. 索引算法原理解析(B-tree以及磁盘存储原理)
  13. ionic:引入图标
  14. 蓝牙BLE方案|伦茨科技-智能直播补光灯方案
  15. oracle中如何转换成数字,如何在Oracle中实现数字进制转换完全版?
  16. Linux:ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cann
  17. java复习之tomcat配置详解
  18. vulnhub靶机Tr0ll1渗透笔记
  19. Linux下配置ip地址
  20. python中cfg_python操作cfg配置文件方式

热门文章

  1. 单片机(51) 什么是编码器?什么是译码器?
  2. arcgis api for javascript 3.33 清空、删除图层
  3. linux驱动程序启动失败,打开程序提示加载驱动失败?三种故障原因及解决方法...
  4. 新萝卜家园 GhostXP SP3 电脑城装机版 V2011.07
  5. graphlan/iTOL画进化树记录
  6. 绝对值线性化的两种方式
  7. 学术会议海报制作和参考
  8. 电脑新固态硬盘ssd安装win7系统教程
  9. Word frequency program 词频分析程序
  10. win10计算机磁盘图标,主编教你win10系统无法正常显示硬盘图标的方法