C++模板的特化超详细精讲

目录
  • 一、泛型编程
  • 二、函数模板
    • 2.1、函数模板的概念
    • 2.2、函数模板的格式
    • 2.3、函数模板的原理
    • 2.4、函数模板的实例化
      • 2.4.1、隐式实例化
      • 2.4.2、显示实例化
  • 三、类模板
    • 3.1、类模板的定义格式
    • 3.1、类模板的实例化
  • 四、模板的特化
    • 4.1、概念
    • 4.2、函数模板特化步骤
    • 4.3、类模板的特化
      • 4.3.1、全特化
      • 4.3.2、偏特化

一、泛型编程

我们前面已经学过函数的重载,实现了在函数名相同的情况下,实现不同的功能!

例如:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}

利用函数重载虽然实现了“通用”的交换函数,但是会有以下问题:

  • 重载的函数仅仅是类型不同,代码复用率低,只要有新的类型时,有需要用户自己增加新的重载函数。
  • 代码的可维护性低,一个出错可能所有的重载都出错。

️️️:在C++中是否存在一个模具,通过给这个模具中填充不同的材料(类型),来获得不同的铸件(即生成具体的代码)。

泛型编程:编写与类型无关的通用代码,是代码复用的一种的手段。模板是泛型编程的基础。

二、函数模板

2.1、函数模板的概念

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

2.2、函数模板的格式

template<typename T1, typename T2,…,typename Tn>

返回值类型 函数名(参数列表){}

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

️注意:typename 是用来定义函数模板参数关键字的,也可以用class,但是不可以用struct。

2.3、函数模板的原理

函数模板我们可以理解为一个蓝图,它本身并不是函数,在编译器编译阶段根据传入的实参类型来推演生成对应类型的函数以供调用,也叫做函数模板的实例化。

2.4、函数模板的实例化

用不同类型的对象使用函数模板时,称为函数模板的实例化。模板参数实例化分为隐式实例化和显示实例化。

2.4.1、隐式实例化

让编译器根据实参推演模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main(){
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);
	//Add(a1, d1);
	Add(a1, (int)d1);
	system("pause");
	return 0;
}

2.4.2、显示实例化

让函数名后的<>中指定模板参数的实际类型。

int main(){
	int a = 10;
	double b = 20.0;
	//Add(a, (int)b);//隐式
	Add<int>(a, b);//显示
	system("pause");
	return 0;
}

Add(a, b);目的在于如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

三、类模板

3.1、类模板的定义格式

template<typename T1, typename T2,…,typename Tn>

class 类模板名

{

//类内成员

};

3.1、类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化传的是类型,而函数模板实例化传的是对象,类模板实例化需要在类模板名字后面跟上<>,然后将实例化类型放在<>中即可!

vector<int> s1;

️:与普通类不同,这里vector是类名,而vector才是类型。

四、模板的特化

4.1、概念

通常情况下,我们使用模板可以实现一些与类型无关的代码,但是对于一些特殊类型可能会得到一些错误的结果,需要特殊处理。

template<class T>
bool Less(T left, T right){
	return left < right;
}
//特化
template<>
bool Less<Date*>(Date* p1, Date* p2)
{
	cout << "调用的模板函数的特化" << endl;
	return *p1 < *p2;
}
//只要是指针  都可以调用这个,这个思想很重要
//偏特化的带限制条件(指针类型可调)
template<class T>
bool Less(T* left, T* right)
{
	cout << "调用的函数模板" << endl;
	return *left < *right;
}

代码解释:当我们在实参部分传的是Date型指针的时候,如果不特化处理,结果会出错。

上述就是在原模板的基础上针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化和类模板特化。

4.2、函数模板特化步骤

  • 必须要先有一个基础的函数模板。
  • 关键字template后面接一队空的<>。
  • 函数名后跟一对<>,尖括号中指定需要特化的类型。
  • 函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<class T>
bool Less(T left, T right){
	return left < right;
}
//特化
template<>
bool Less<Date*>(Date* p1, Date* p2)
{
	cout << "调用的模板函数的特化" << endl;
	return *p1 < *p2;
}

4.3、类模板的特化

4.3.1、全特化

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

//类模板
template<class T1, class T2>
class data
{
public:
	data(T1 a, T2 b)
		:_a(a)
		, _b(b)
	{
		cout << "data<T1, T2>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};
//全特化
template<>
class data <int, char>
{
public:
	data(int a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "全特化data<int, char>" << endl;
	}
private:
	int _a;
	char _b;
};
int main()
{
	data<int, int> s1(1, 2);
	data<int, char> s2(1, 'a');
}

4.3.2、偏特化

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

//类模板
template<class T1, class T2>
class data{
public:
	data(T1 a, T2 b)
		:_a(a)
		, _b(b)
	{
		cout << "data<T1, T2>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};

️️️偏特化有以下两种变现方式:

部分特化:将模板参数中的一部分参数特化

//半特化
// 1、将模板参数类表中的一部分参数特化。
template<class T1>
class data<T1, char> {
public:
	data(T1 a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "偏特化data<T1, char>" << endl;
	}
private:
	T1 _a;
	char _b;
};

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

// 2、偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
template<class T1, class T2>
class data <T1*, T2* > {
public:
	data(T1 a, char b)
		:_a(a)
		, _b(b)
	{
		cout << "Data<T1*, T2*>" << endl;
	}
private:
	T1 _a;
	T2 _b;
};
template<class T1, class T2>
class data<T1&, T2&>
{
public:
	data(const T1& a, const T2& b)
		:_a(a)
		, _b(b)
	{ cout << "Data<T1&, T2&>" << endl; }
private:
	const T1& _a;
	const T2& _b;
};
int main(){
	data<char*, char*> s6(1,2);
	data<int&, int&> s7(1, 2);
	system("pause");
	return 0;
}

代码解释:

  • 第一个是两个参数特例化为指针类型(只要传的是指针,就走对应的)。
  • 第二个是两个参数特例化为引用类型。

到此这篇关于C++模板的特化超详细精讲的文章就介绍到这了,更多相关C++模板特化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何C++使用模板特化功能

    目录 前言: 1.函数模板的特化 2.类模板的特化 前言: 通过定义模板,使得函数或者类不依赖于特定的类型,这样大幅提升了代码的复用性. 然而,不管是类模板还是函数模板,对所有的类型都是采用相同的处理方式(同一份代码).为此,C++提出了“模板特化的概念”,对特定类型提供模板的改造. 比如,对于函数模板来说,可以通过特化指定特定的类型,针对此特定类型对函数体内容进行重写,使得函数对特定类型实现特定功能. 注意: 按照语法,对函数模板和类模板进行特化后,就变为普通函数和普通的类,而不再是模板. 1

  • C++ 类模板、函数模板全特化、偏特化的使用

    一.类模板全特化.偏特化 #pragma once #include <iostream> #include <map> template <typename T, typename U> class TC { public: TC() { std::cout << "泛化版本构造函数" << std::endl; } void funtest() { std::cout << "泛化版本成员函数&quo

  • 解读C++编程中类模板的三种特化

    1.类模板显式特化 为了进行特化,首先需要一个通用的版本,称主模板.主模板使用了标准库堆算法.  堆 是一种线性化的树形结构,将一个值压入一个堆中, 实际上等于将该值插入到一个树形结构中;将一个值从堆中取出就等于移除并返回堆中最大值.但在处理字符的指针时会碰钉子.堆将按照指针的值进行组织. 我们可以提供一个显式特化版本解决此问题(例1)如果希望除了一个针对const char*的Heap外,还希望提供一个针对char *的Heap;(例2) //主模板 template <typename T>

  • C++模板之特化与偏特化详解

    前言 说到C++模板,这个已经不是什么新东西了,自己在实际开发中也用过:对于C++模板特化和偏特化,对于别人来说,已经不是什么新东西了,但是对于我来说,的确是我的盲区,那天在群里讨论这个问题,自己对于这部分确实没有掌握,又联想到在<STL源码剖析>一书中,对于此也是有着介绍.所以,今天就对此进行详细的总结,以备后忘. C++模板 说到C++模板特化与偏特化,就不得不简要的先说说C++中的模板.我们都知道,强类型的程序设计迫使我们为逻辑结构相同而具体数据类型不同的对象编写模式一致的代码,而无法抽

  • 深入分析C++模板特化与偏特化

    1.模板特化 1.1概述 模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化.模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化. 1.2函数模板特化 函数模板特化是在一个统一的函数模板不能在所有类型实例下正常工作时,需要定义类型参数在实例化为特定类型时函数模板的特定实现版本.查看如下例子. #include <iostream> using namespace std; template<type

  • C++模板的特化超详细精讲

    目录 一.泛型编程 二.函数模板 2.1.函数模板的概念 2.2.函数模板的格式 2.3.函数模板的原理 2.4.函数模板的实例化 2.4.1.隐式实例化 2.4.2.显示实例化 三.类模板 3.1.类模板的定义格式 3.1.类模板的实例化 四.模板的特化 4.1.概念 4.2.函数模板特化步骤 4.3.类模板的特化 4.3.1.全特化 4.3.2.偏特化 一.泛型编程 我们前面已经学过函数的重载,实现了在函数名相同的情况下,实现不同的功能! 例如: void Swap(int& left, i

  • Java超详细精讲数据结构之bfs与双端队列

    目录 一.bfs 二.双端队列 三.算法题 1.kotori和迷宫 2.小红找红点 3.小红玩数组 一.bfs bfs(广度优先搜索),类似二叉树的层序遍历,利用队列完成.一般用于求最短路. 图的最短路问题: 给定一个无向图,每条边的长度都是1.求1号点到x号点的最短距离. 顶点数n 边数为m q次询问 输入x 输出1到x的最短距离. 若1号点到x不连通,则输出-1 二.双端队列 双端队列的应用(区间翻转): 对于长度为n的数组,给定一个长度为m的区间,区间初始位置为a[1]到a[m]. 3种操

  • C++可扩展性与多线程超详细精讲

    目录 一.可扩展性和多线程 二.线程示例 一.可扩展性和多线程 基于 Boost.Asio 之类的库开发程序与通常的 C++ 风格不同.可能需要更长时间才能返回的函数不再按顺序调用. Boost.Asio 不调用阻塞函数,而是启动异步操作.操作完成后应该调用的函数现在在相应的处理程序中调用.这种方法的缺点是顺序执行函数的物理分离,这会使代码更难理解. 诸如 Boost.Asio 之类的库通常用于实现更高的效率.无需等待操作完成,程序可以在其间执行其他任务.因此,可以启动多个同时执行的异步操作——

  • C++ Boost CircularBuffer算法超详细精讲

    提要 库 Boost.CircularBuffer 提供了一个循环缓冲区,它是一个具有以下两个基本属性的容器: 循环缓冲区的容量是恒定的,由您设置.当您调用成员函数(例如 push_back())时,容量不会自动更改.只有您可以更改循环缓冲区的容量.循环缓冲区的大小不能超过您设置的容量. 尽管容量不变,但您可以随时调用 push_back() 将元素插入循环缓冲区.如果已达到最大大小并且循环缓冲区已满,则将覆盖元素. 当可用内存量有限并且您需要防止容器任意增长时,循环缓冲区是有意义的.另一个例子

  • C++ Boost Graph算法超详细精讲

    Boost.Graph 中的算法类似于标准库中的算法——它们是通用的并且非常灵活.但是,并不总是很清楚应该如何使用它们. 示例 31.8.使用breadth_first_search() 从内到外访问点 #include <boost/graph/adjacency_list.hpp> #include <boost/graph/breadth_first_search.hpp> #include <boost/graph/named_function_params.hpp&

  • C++ Boost Algorithm算法超详细精讲

    目录 一.说明Boost.Algorithm 二.示例 练习 一.说明Boost.Algorithm Boost.Algorithm 请注意,其他 Boost 库提供了许多算法.例如,您会在 Boost.StringAlgorithms 中找到处理字符串的算法. Boost.Algorithm 提供的算法不受特定类的约束,例如 std::string.与标准库中的算法一样,它们可以与任何容器一起使用. 二.示例 示例 29.1.使用 boost::algorithm::one_of_equal(

  • Java BOI与NIO超详细实例精讲

    目录 Java BIO 示例代码 Java NIO 代码解读 Java BIO 阻塞IO,每个客户端链接都需要一个独立的线程处理,客户端链接没关闭时,线程链接处于阻塞状态,直到客户端链接关闭 如果客户端链接没有读取到数据,链接就会一直阻塞住,造成资源浪费 示例代码 开发一个ServerSocket服务端,通过telnet链接发送信息 import java.io.IOException; import java.io.InputStream; import java.net.ServerSock

  • Go Web 编程中的模板库应用指南(超详细)

    如果你有过Web编程的经验,那么或多或少都听说过或者使用过模板.简而言之,模板是可用于创建动态内容的文本文件.例如,你有一个网站导航栏的模板,其中动态内容的一部分可能是根据当前用户是否登录显示登录还是退出按钮. Go提供了两个模板库 text/template和 html/template.这两个模板库的使用方式是相同的,但是 html/template包在渲染页面模板时会在后台进行一些编码以帮助防止造成代码注入(XSS 攻击). 因为两个模板库都使用相同的接口,因此本文中介绍的所有内容均可用于

  • C++编程模板匹配超详细的识别手写数字实现示例

    首先,本篇文章用到的方法是模板匹配,而不是基于神经网络的,还请各位注意了!(模板匹配还请自行了解,站上有很多介绍)我刚开始做实验的时候只有一点c++基础,对于文件和opencv我一点都不了解,所以导致了我刚开始迷茫了很久,直到后来才渐渐做起来.废话不多说,让我们开始吧! 过程很简单,如下: 匹配成功:存在一个最小距离(这些距离相等),且为一个数字:存在多个最小距离,且为同一个数字. 拒绝识别:存在多个最小距离,且为不同数字. 识别错误:存在一个最小距离,但与被测数字不是相同的数字. 也许乍一看看

  • C++超详细讲解树与二叉树

    目录 树 树的定义 树的名词解释 树的表示 树的存储结构 二叉树的概念及结构 二叉树的概念 二叉树的性质 二叉树的存储结构 顺序存储结构 链式存储结构 树 树的定义 Q:什么是树 A:树是一种 非线性 的数据结构,它是由 n ( n>=0 )个有限结点组成一个具有层次关系的集合.把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的. Q:树有什么特点 有一个特殊的结点,称为根结点,根节点没有前驱结点. 除根节点外,其余结点被分成M(M>0)个互不相交的集合T1.T2.…….T

随机推荐