C++语法---模板进阶知识

绪论​
“那些看似不起波澜的日复一日,会在某天让你看到坚持的意义。”本篇文章主要写到非类型的模板参数、模板的特化、模板的分离编译问题、以及适配器和仿函数的使用讲解,在之前已经将模板的基本使用进行了学习(可见c++模板)话不多说安全带系好,发车啦(建议电脑观看)。

思维导图:

附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


1.非类型模板参数

在模板参数中分为了类型模板参数和非类型模板参数

类型模板参数:出现在模板中在class/typename后的参数类型名称
非类型模板参数用一个常量作为类的一个模板参数,在类中就能直接使用这个常量

例如,在一个类中成员变量为一个静态数组时,我们可以通过这个常量来控制该数组的大小:

template<class T,size_t N>
class Aarry
{
private:
	T base[N];
};

int main()
{
	Aarry<int, 5> a;
	return 0;
}

在这里插入图片描述
注意的是:

  1. 该非类型的模板参数支持整形(浮点型不可以,在c++11及以前暂时不能使用)
  2. 且只能是常量,而不能为变量

2.模板的特化

模板的特化分为两种:

  1. 函数模板特化
  2. 类模板特化

使用方法不同通过具体实例来看:

2.1 对于类的特化:

template<class T , class Z>
class A
{
public:
	A() { cout << "class A<T,Z>" << endl; }

private:
	T _a;
	Z _z;
};

//注意下方!!!!
//此处就是一个类的**全特化**(所有模板参数都进行了特化)
//方法如下就是在类旁边加上特化的类型
template<>
class A<int,double>
{
public:
	A() { cout << "class A<int,double>" << endl; }

private:
	int _a;
	double _z;
};

template<class T1>
class C<T1,double>
{
public:
	C() { cout << "class C<T1,double>" << endl; }

private:
	T1 _a;
	double _z;
};




int main()
{

	A<int,double> a1;//调用特化的类
	A<int,int> a1;//调用特化的类
	A<int*, int*> a2;//正常实例化对象
	
	return 0;
}

运行结果为:在这里插入图片描述
类模板的特化(特化的前提是得 有原模板)来说分为:

  1. 全特化:
    从上方也能看出方法为:
    在这里插入图片描述
    也就是将特化的类型写在类名旁边进行特化操作
  2. 偏特化
    特化一部分初参数类型,还剩一部分类型仍然为模板参数
    方法为:
    在这里插入图片描述
    同样也就是将特化的类型写在类名旁边进行特化操作,而对于不想特化的就仍然写成模板参数

注意如果有歧义那么编译器会找最匹配的(如全特化和偏特化同时有部分特化相等,当全特化更加匹配时那么就会走全特化,具体如下面这种情况那么就会走到全特化
在这里插入图片描述
此处上面的例子就是全特化更加匹配:
全特化在这里插入图片描述
在这里插入图片描述

偏特化的特别使用,对参数的限制

  1. 此处当T1、T2是指针类型时会走该特化类:
template <class T1,class T2>
class Date<T1*,T2*>//此处当T1、T2是指针时走这里!!
{
public:
	Date() { cout << "Data<T1*, T2*>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

在这里插入图片描述

  1. 此处当T1、T2是引用类型时会走该特化类:
template <class T1, class T2>
class Date<T1&, T2&>
{
public:
	Date() { cout << "Data<T1&, T2&>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

在这里插入图片描述


2.2函数模板的特化

对于函数的特化见于下面例子:

template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	Date* d1 = new Date(2000, 10, 1);
	Date* d2 = new Date(2000, 10, 2);
	
	cout << Less(d1, d2) << endl;
//预计为日期d1 < 日期d2 返回打印1
	return 0;
}

上面程序结果为:
在这里插入图片描述
在这里插入图片描述

我们预期的结果是1(日期d1 < 日期d2),可发现这个结果是不确定的(可能1/0),分析发现这是因为Less函数接收到的T类型为Date*(指针类型)而d1、d2的地址每次分配都不一样 可大可小,对此这里我们应该是要取到*d1 与 *d2 进行比较而不是d1 与 d2 比,所以我们应该对这种情况进行特殊化处理(函数特化)具体如下:

//特化方法如下:
template<>
bool Less<Date*>(Date* left, Date* right)//当接收到Date*类型时进行特殊化处理
{
	return *left < *right;//去取日期来比较 而非地址
}

通过上面能看出函数特化的写法:

在这里插入图片描述
在有原函数模板的前提下,特化一个函数的方法:
1. 写下template<> ,2. 再将特化的类型用尖括号(<>)写到函数名旁边,3. 并且还要改变函数内所用到该类型的类型名

注意的特殊点
在这里插入图片描述
也就是此处的const是要修饰变量本身left、right(&left、&right),而不是*left、right 所以把const挪到后面
理解如同: const int * p (修饰指针
p)与 int * const p(修饰变量p)

类模板用特化,函数模板不用特化用重载(编译器一般会用最适配的,故有更匹配的重载时,会直接调用该函数)!

3.模板的分离编译问题

分离编译:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

具体:当对有模板的函数进行分离编译时会出现下面问题:

//在 Add.cpp 的函数实现
template<class T>
T Add(const T& t1, const T& t2)
{
	return t1 + t2;
}
//在 Add.h 头文件中的函数声明
template<class T>
T Add(const T& t1, const T& t2);//类函数的分离编译

//在 main.cpp 中
int main()
{
	cout << Add(1,2) << endl;
	return 0;
}

在这里插入图片描述
反观普通函数就能正常跑!

//fun.cpp
void fun()
{
	cout << "void fun()" << endl;
}
//fun.h
void fun();//普通函数的分离编译

//main.cpp
int main()
{
	fun();
	//cout << Add(1,2) << endl;
	return 0;
}

在这里插入图片描述
对此发现找不到Add函数主要是因为模板的原因,那为什么会加上模板就不行了嘞??
编译器编译的步骤是:

  1. 预处理(展开头文件、替换宏、条件编译) test.i
  2. 编译(检查语法、生成汇编)test.s
  3. 汇编(将汇编代码转换为二进制机器码)test.o
  4. 链接 (将多个.o的目标文件合并)a.out

此处主要是因为对于模板函数来说,他在运行阶段时才会实例化产生地址。因此在编译阶段始终都是没有地址的,而编译时会给Add函数变成汇编指令(call Add(地址?)),一般分离编译的函数在链接时就会通过合并符号表(存放着符号汇总(汇总一些全局变量和函数)的符号)将此处的地址补齐(此处具体可见博客),而模板函数因为无实际地址就无法进行补齐。

解决方案:

  1. 显示实例化:意思就如名字一样进行对应所需的实例化告诉编译器类型如下:在这里插入图片描述但缺点也很明显,当类型变成其他时就需要再写一个 (template double Add < double > … )
  2. 把分离编译的模板函数的实现和声明一起写在同一个头文件里(有些地方会把.h 改成 .hpp),这样当预处理时就会把实现也带进main.cpp中当Add编译时就会找到实现,那么就会直接进行实例化生成地址,不再依赖于链接

4.模板的缺点

  1. 导致代码膨胀问题,同时会导致编译时间变长
  2. 模板出现编译错误时,错误信息非常难看(方法:先看第一个错误),定位不准

5.适配器

适配器是一种设计模式,常用于一些可以套用的结构中,如stack、queue他们底层都需要一个存储数据的容器,而这个容器就能通过适配器来直接调用的来使用,不需要再自行实现。如stack他的适配器就能是vector、list、dequeue。
而适配器的位置常常是写在模板参数处的
在这里插入图片描述
基本的使用方法:创建对象后直接使用该对象即可!
在这里插入图片描述
如:插入数据
在这里插入图片描述

6.仿函数

仿函数:一种类似于函数的类
仿函数的创建,创建一个类,然后在类中重载operator()
仿函数的使用:通过对象调用operator() ,达到模仿函数的样子去使用(本质还是调用了类中的运算符重载函数)。
在这里插入图片描述

  1. 构建成对象,通过实例化对象后用对象来使用(见上 图标注1),
    1.1传递给某写函数当函数参数(类似于函数指针!)(如下改变sort的第三个参数就会有不同的效果,实际上把对象传过去在该函数中使用这个仿函数)在这里插入图片描述less排升序(此处模拟实现less(Less))在这里插入图片描述greater排降序
    在这里插入图片描述
    2.当成模板参数传给某类,在类内部通过实例化对象后再直接使用
    具体如下:
    在这里插入图片描述在这里插入图片描述

总结来说仿函数就是一个类似于函数的类,我们只需要将其实例化对象后==通过对象()==就能使用这个仿函数

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量C++细致内容,早关注不迷路,我们下章再会。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/136515.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

fastANI-基因组平均核酸一致性(ANI)计算

文章目录 简介安装使用Many to Man-使用基因组路径作为输入One to One 结果其他参数说明可视化两个基因组之间的保守区域并行化 简介 FastANI 是为快速计算全基因组平均核苷酸同一性&#xff08;Average Nucleotide Identity&#xff0c;ANI&#xff09;而开发的&#xff0c;无…

【学习笔记】Understanding LSTM Networks

Understanding LSTM Networks 前言Recurrent Neural NetworksThe Problem of Long-Term DependenciesLSTM Networks The Core Idea Behind LSTMsStep-by-Step LSTM Walk ThroughForget Gate LayerInput Gate LayerOutput Gate Layer Variants on Long Short Term MemoryConclus…

java--JDBC学习

文章目录 今日内容0 复习昨日1 JDBC概述2 JDBC开发步骤2.1 创建java项目2.2 导入mysql驱动包2.2.1 复制粘贴版本2.2.2 idea导入类库版本 2.3 JDBC编程 3 完成增删改3.1 插入3.2 更新3.3 删除 4 查询结果集ResultSet【重要】5 登录案例【重要】6 作业 今日内容 0 复习昨日 1 JDB…

二十五、城市建成区结果制图——复杂图的制作

一、前言 有些时候看到一些参考文献中有些很复杂的图,例如多幅合并在一起,其实这种图本质上就是单一的图合并在一起,然后将其导出即可。 二、具体操作 其实对于制图必备要素的添加就不过多介绍,主要介绍有什么办法保持图形之间一致性,例如,其图例、指北针、比例尺统一…

vColorPicker与vue3-colorPicker——基于 Vue 的颜色选择器插件

文章目录 前言样例特点 一、使用步骤&#xff1f;1. 安装2.引入3.在项目中使用 vcolorpicker 二、选项三、事件四、问题反馈问题所在安装引入例子效果图 前言 vColorPicker——官网 vColorPicker——GitHub 样例 vColorPicker是基于 Vue 的一款颜色选择器插件&#xff0c;仿照…

自定义Graph Component:1-开发指南

可以使用自定义NLU组件和策略扩展Rasa&#xff0c;本文提供了如何开发自己的自定义Graph Component指南。   Rasa提供各种开箱即用的NLU组件和策略。可以使用自定义Graph Component对其进行自定义或从头开始创建自己的组件。   要在Rasa中使用自定义Graph Component&#x…

Oracle(2-1) Networking Overview

文章目录 一、基础知识1、Network Environ Challenges 网络环境挑战2、Simple Network :2-Tier 简单的两层网络3、Simple to Complex : N-Tier 简单到复杂&#xff1a;N层网络4、Oracle Network Solutions Oracle网络解决方案5、Key Features of Oracle Net Oracle Net的主要功…

Linux必备:这十个流程图让你变的更强!

图是我们与信息联系并处理其重要性的绝佳方法&#xff1b;它们有助于传达关系和抽取信息&#xff0c;并使我们能够可视化概念。 从基本工作流程图到复杂的网络图&#xff0c;组织图&#xff0c;BPMN&#xff08;业务过程模型和符号&#xff09;&#xff0c;UML图等等&#xff0…

关于值传递和引用传递的问题记录

目录 1. 问题概述 1.1 测试 1.2 结果 2. ArrayList和Arrays.ArrayList 1. 问题概述 最近忙着写论文很久没更新了&#xff0c;趁现在有时间简单记录一下最近遇到的一个坑。 对于Java中的List<>类型的对象&#xff0c;按我以前理解是引用传递&#xff0c;但有一点要注…

第一百七十回 Material3中的IconButton

文章目录 1. 概念介绍2. 使用方法2.1 filled风格2.2 filledTonal风格2.3 outlined风格 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"如何修改NavigationBar组件的形状"相关的内容&#xff0c;本章回中将 介绍IconButtion组件.闲话休…

【入门Flink】- 10基于时间的双流联合(join)

统计固定时间内两条流数据的匹配情况&#xff0c;需要自定义来实现——可以用窗口&#xff08;window&#xff09;来表示。为了更方便地实现基于时间的合流操作&#xff0c;Flink 的 DataStrema API 提供了内置的 join 算子。 窗口联结&#xff08;Window Join&#xff09; 一…

数据结构—内部排序(上)

文章目录 8.内部排序(上)(1).排序基础#1.为什么是内部排序#2.排序的稳定性 (2).冒泡排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (3).选择排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (4).插入排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (5).希…

vue+mongodb+nodejs实现表单增删改查

ExpressMongodbVue实现增删改查 效果图 前言 最近一直想学下node,毕竟会node的前端更有市场。但是光看不练&#xff0c;感觉还是少了点什么&#xff0c;就去github上看别人写的项目&#xff0c;收获颇丰&#xff0c;于是准备自己照葫芦画瓢写一个。 作为程序员&#xff0c;一…

Flutter:改变手机状态栏颜色,与appBar状态颜色抱持一致

前言 最近在搞app的开发&#xff0c;本来没怎么注意appBar与手机状态栏颜色的问题。但是朋友一说才注意到这两种的颜色是不一样的。 我的app 京东 qq音乐 这样一对比发现是有的丑啊&#xff0c;那么如何实现呢&#xff1f; 实现 怎么说呢&#xff0c;真不会。百度到的一些是…

Android开发之apk瘦身计划

为什么apk越来越大&#xff1f; 1.项目不断发展&#xff0c;功能越多&#xff0c;代码量增加的同时&#xff0c;资源文件也在不断的增多。 2.app支持的主流dpi越来越多&#xff0c;如ldpi、mdpi、hdpi、xh xxh xxxh等等&#xff0c;间接导致资源增多。 3.引入的第三方sdk或开…

软件测试面试-银行篇

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

从系统层到应用层,vivo 已在安全生态层

你每隔多久就会使用一次手机&#xff1f;调研结果也许会让你大吃一惊。 权威报告数据显示&#xff0c;2022年&#xff0c;24.9%的受访者每日使用手机时长超过10小时&#xff0c;其中3.8%的受访者“机不离手”&#xff0c;每日使用时长超过15小时。而真正让手机化身为时间吞金兽…

go学习之接口知识

文章目录 接口1.接口案例代码展示2.基本介绍3.基本语法4.应用场景介绍5.注意事项和细节6.接口编程经典案例7.接口与继承之间的比较8.面向对象编程--多态1&#xff09;基本介绍2&#xff09;快速入门3&#xff09;接口体现多态的两种形式 9.类型断言1&#xff09;先看一个需求2&…

【第六章】软件设计师 之 数据结构与算法基础

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、数据结构与算法基础 2、数据 3、稀疏矩阵…

分享一下关于“vcruntime140_1.dll丢失的5种解决方法

今天我来给大家分享一下关于“vcruntime140_1.dll丢失的5种修复方法”的分享。首先&#xff0c;我们来了解一下vcruntime140_1.dll丢失的原因。 病毒感染&#xff1a;病毒或恶意软件可能损坏或删除vcruntime140_1.dll文件。 系统更新或软件安装&#xff1a;在进行系统更新或安…