条款47:请使用traits classes表现类型信息
1.前言
STL主要由“用以表现容器,迭代器和算法”的template构成,但也覆盖若干工具性templates,其中一个名为advance,用来将某个迭代器移动某个给定距离:
tempalte<typename IterT,typename DistT>//将迭代器向前移动d单位
void advance(IterT& iter,DistT d);//如果d<0,则向后移动
观念上advance只是做iter+=d动作,但其实不可以全然是那样,因为只有random access(随机访问)迭代器才支持+=操作。面对其它迭代器种类,advance必须反复施行++或--,共d次。
先回顾下STL迭代器的分类:STL共有5种迭代器分类,inpiut迭代器只能向前移动,一次一步,客户可只读取它们所指的变量,而且只读取一次,他们模仿指向输入文件的阅读指针(read pointer);c++程序库中的istream_iterators是这一分类的代表。output迭代器情况类似,但一切只为输出:它们只向前移动,一次一步,客户只涂写一次。它们模仿指向输出文件的涂写指针(write pointer);ostream_iterators是这一分类的代表。这是功能最小的两个迭代器分类。由于这;两类都只能向前移动,而且只能读或写其所指物最多一次,所以它们只适合“一次性操作”(one-pass algorithms)。另一个功能比较强大的分类是forward迭代器。这种迭代器可以做前述两种分类所能做的每一件事,而且可以读或写其所指物一次以上。这使得它们可以施行于多次性操作算法(multi-pass algorithms)。STL并未提供单向linked list,但某些程序库有slit,而置入这种容器的迭代器就是属于forward迭代器。指入TR1 hashed容器的也可能是这一分类。
Bidirectional迭代器比上一个功能更大:它除了可以向前移动,还可以向后移动。STL的list迭代器就属于这一类,set,multiset,map和multimap的迭代器也都是这一分类。
最强大的迭代器当属random access迭代器。这种迭代器功能强大之处再与它可以执行“迭代器算术”,即可以在常量时间内向前或向后跳跃任意距离。
对这5种分类,c++标准程序库分别提供专属的卷标结构(tag struct)加以确认:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag:public input_iterator_tag{};
struct bidirectional_iterator_tag:public forward_iterator_tag{};
struct random_access_iterator_tag:public biairectional_iterator_tag{};
这些struct之间的继承关系是有效的is-a。
现在回到advance函数,我们知道STL迭代器有着不同的能力,实现advance的策略之一是采用“最低但最普及”的迭代能力,以循环往复递增或递减迭代器。然而这种做法耗费线性时间,我们知道random access迭代器支持迭代器算术运算,只耗费常量时间,如果面对这种迭代器,我们希望运用其优势:
然而我们真正希望的是以这种方式实现advance:
template<typename IterT,typename DistT>
void advance(IterT& iter,DistT d)
{if(iterT& iter,DistT d){if(iter is a random access iterator){iter+=d;//针对random access迭代器使用迭代器算术运算}else{if(d>=0){while(d--) ++iter;}else{if(d>=0){while(d--) ++iter;}else {while(d++) --iter;}//针对其它迭代器分类,反复调用++或--}}}}
这种做法首先是必须判断iter是否未random access迭代器,也就是说需要知道类型IterT是否为random access迭代器。换句话说我们需要取得类型的某些信息。那就是traits让你得以进行的事情:它们允许你在编译期间取得某些类型信息。。
2.实例分析
traits并不是c++关键自或一个预先定义好的构件;他们是一种技术,也是一个c++程序员共同遵守的协议。这个技术的要求之一是他对内置(built-in)类型或用户自定义类型的表现必须一样好。举个例子,如果上述advance仍然必须有效运作,那意味这traits技术必须i能够施行于内置类型如指针上。
”traits必须能够施行于内置类型“意味”类型内的嵌套信息(nesting information)“这种东西必须出局了,因为我们无法将信息嵌套于原始指针内。因此类型的traits信息必须位于类型自身之外。标准技术时把它放进一个template及其一个被命名为iterator_traits:
template<typename iterT>//template,用来处理
struct iterator_traits;//迭代器分类的相关信息
正如所看到的,iterattor_traits是个struct。习惯上traits总是被实现为structs,但它们又往往被称为traits classes。
iterator_traits的运作方式是针对每一个类型IterT,针对每一个类型IterT,在struct iterator_traits<IterT>内一定声明某个typedef名为iiterator_category,这个typedef用来确认IterT的迭代器分类。
iterator_traits以两个部分实现上述所言。首先它要求每一个“用户自定义的迭代器类型”必须嵌套一个typedef,名为iterator_category,用来确认适当的卷标结构。例如deque的迭代器可随机访问,所以一个针对deque迭代器而设计的class看起来会是这个样子:
template<...>
class deque{public:class iterator{public:typedef random_access_iterator_tag iterator_category;....};....
};
list的迭代器可双向行进,所以它们应该是这样的:
template<...>
class list{public:class iterator{public:typedef bidirectional_iterator_tag iterator_category;....};....
};
至于iterator_traits,只是相应iterator class的嵌套式typedef:
//类型IterT的iterator_category其实是用来表现“IterT说它自己是什么”
//关于“typedef typename”的运用,见条款42
template<typename IterT>
struct iterator_traits{typedef typename IterT::iterator_category iterator_category;...
};
这对用户自定义类型行得通,但对指针(也是一种迭代器)行不通,因为指针不可能嵌套tepedef。iterator_traits的第二部分如下,专门用来对付指针:
为了支持指针迭代器,iterator_traits特别针对指针类型提供一个偏特化版本(partial template specialization)。由于指针的行径与random access迭代器类似,所以iterator_traits为指针指定的迭代器类型是:
template<typename IterT>//template偏特化
struct iterator_traits<IterT*>//针对内置指针
{typedef random_access_iterator_tag iterator_category;....
};
现在,我们应该了解了如何设计并实现一个traits class了:
(1)确认若干你希望可取的的类型相关信息。例如对迭代器而言,我们希望将来可取其分类(category);
(2)为该信息选择一个名称(比如iterator_category)
(3)提供一个template和一组特化版本,内含你希望支持的类型相关信息。
现在有了iterator_traits(实际上是std::iterator_traits,因为它是c++标准程序的一部分),我们可以对advance实践先前的伪码(pseudocode):
template<typename IterT,typename DistT>
void advance(IterT& iter,DistT d)
{if(typeid(typename std::iterator_traits<IterT>::iterator_category)==typeid(std::random_access_iterator_tag))....
}
虽然这看起来前景光明,但并不是我们想要的。首先会导致编译问题。IterT类型在编译器间获知,所以iterator_traits<IterT>::iterator_category也可以在编译器间确定。但if语句却是在运行期才会核定。
我们真正想要的是一个条件式,判断“编译器核定成功”之类型。恰巧c++有一个取得这种行为的办法,那就是重载(overloading)。
当我们重载某个函数f,必须详细描述各个重载建房的参数类型。当你调用f,编译器便根据传来的实参选择最适合的重载件。编译器的态度是“如果这个重载件最匹配传递过来的实参,就调用这个f;如果那个重载件最匹配,就调用那个f;如果第三个f最匹配,就调用第三个f”。依次类推。这正是一个针对类型而发生的“编译器条件句”。为了让advance的行为如我们所期望的那样,我们需要做的是产生两版重载函数,内含advance的本质内容,但各自接受不同类型的iterator_category对象。我将这两个函数取名为doAdvance:
template<typename IterT,typename DistT>//用于random access 迭代器
void doAdvance(IterT& iter,typename Dist,std::random_access_iterator_tag)
{iter+=d;
}template<typename IterT,typename DistT,std::bidirectional_iterator_tag>//这份
{ //实现用于bidirectional迭代器if(d>=0){while(d--)++iter;}else{while(d++) --iter;}}
template<typename IterT,typename DistT>//这份用于实现input迭代器
void doAdvance(IterT& iter,DistT d,std::input_iterator_tag)
{if(d<0){throw std::out_of_range("Negative distance");}while(d--) ++iter;
}
由于forward_iterator继承自input_iterator_tag,所以上述doAdvance的input_iterator_tag版本也能够处理forward迭代器。这是iterator_tag structs继承关系带来的一项红利。实际上这也是public继承带来的部分好处:针对base class编写的代码用于derived class身上行得通。
advance函数规范说,如果面对的是random access和bidirectional迭代器,则接受正距离和负距离;但如果面对的是forward或input迭代器,则移动负距离会导致不明确(未定义)行为。我所检验过的实现码都假设d不为负,于是直接进入一个冗长的循环迭代,等待计数器降为0。上述异常我以抛出异常取而代之。
有了这些doAdvance重载版本,advance需要做的是调用它们并额外传递一个对象,后者必须带有适当的迭代器分类。于是编译器运用重载解析机制(overloading resolution)调用适当的实现代码:
template<typename IterT,typename DistT>
void advance(IterT& iter,DistT d)
{doAdvance(iter,d,typename std::iterator_traits<IterT>::iterator_category());
}
现在我们可以总结如何使用一个traits class了:
(1)建立一组重载函数或函数模板,,彼此间的差异只在于各自的traits参数。令每个函数实现码与其接受之traits信息相应和。
(2)建立一个控制函数或函数模板,它调用上述那些劳工函数并传递traits class所提供的信息。
traits广泛用于标准程序库,其中当然有上述讨论的iterator_traits,除了供应iterator_category还供应另四分迭代器相关信息。此外还有char_traits用来保存字符类型相关信息,以及numeric_limits用来保存数值类型的相关信息,例如某些数值类型可表现之最小值和最大值等等;命名为numeric_limits有点让人惊讶,因为traits classes的名称以"traits"结束,但numeric_limits却没有遵守这种风格。
3.总结
(1)Traits classes使得“类型相关信息”在编译器可用。它们以template和“templates特化”完成实现。
(2)整合重载技术后,traits classes有可能在编译器对类型执行if...else 测试。
相关文章:
条款47:请使用traits classes表现类型信息
1.前言 STL主要由“用以表现容器,迭代器和算法”的template构成,但也覆盖若干工具性templates,其中一个名为advance,用来将某个迭代器移动某个给定距离: tempalte<typename IterT,typename DistT>//将迭代器向…...
蓝桥杯省赛无忧 课件49 DFS-剪枝
01 数字王国之军训排队 02 特殊的三角形 03 特殊的多边形...
Linux中查看端口被哪个进程占用、进程调用的配置文件、目录等
1.查看被占用的端口的进程,netstat/ss -antulp | grep :端口号 2.通过上面的命令就可以列出,这个端口被哪些应用程序所占用,然后找到对应的进程PID https://img-blog.csdnimg.cn/c375eb2bed754426b373907acaa7346e.png 3.根据PID查询进程。…...
大模型面试题总结
文章目录 一、大模型(LLMs)基础面二、大模型(LLMs)进阶面三、大模型(LLMs)微调面四、大模型(LLMs)langchain面1. 基于LLM+向量库的文档对话 基础面2. 基于LLM+向量库的文档对话 优化面3. LangChain的概念面试问题4.LangChain的一些模块提问5.LangChain的业务提问6.Lang…...
Authorization Failed You can close this page and return to the IDE
一.问题描述 注册JetBrains成功,并且通过了学生认证,但在activate pycharm时,却显示Authorization Failed You can close this page and return to the IDE如上图 二.原因: 可能是因为之前使用了破解版pycharm 三.解决方法&am…...
【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建
系列文章目录 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用 在一个人体姿态估计的任务中,需要用深度学习模型…...
《设计模式的艺术》笔记 - 策略模式
介绍 策略模式定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为模式。 实现 myclass.h // // Created by yuwp on 2024/1/12. //#ifndef DES…...
【Elasticsearch篇】详解使用RestClient操作索引库的相关操作
文章目录 🍔什么是Elasticsearch🌺什么是RestClient🎆代码操作⭐初始化RestClient⭐使用RestClient操作索引库⭐使用RestClient删除索引库⭐使用RestClient判断索引库是否存在 🍔什么是Elasticsearch Elasticsearch是一个开源的分…...
ES数据处理方法
由于日志数据存在ES项目里,需要从ES中获取日志进行分析,使用SQL数据进行处理,如下: select traceid-- STRING COMMENT 流程id, ,appnum -- BIGINT COMMENT 迭代号, ,appversion --STRING COMMENT APP版本, ,appc…...
STM32实现软件IIC协议操作OLED显示屏(2)
时间记录:2024/1/27 一、OLED相关介绍 (1)显示分辨率128*64点阵 (2)IIC作为从机的地址0x78 (3)操作步骤:主机先发送IIC起始信号S,然后发送OLED的地址0x78,然…...
【linux】远程桌面连接到Debian
远程桌面连接到Debian系统,可以使用以下几种工具: 1. VNC (Virtual Network Computing) VNC(Virtual Network Computing)是一种流行的远程桌面解决方案,它使用RFB(Remote Framebuffer Protocol࿰…...
python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-菜单管理实现
锋哥原创的SpringbootLayui python222网站实战: python222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火…...
JS之隐式转换与布尔判定
大家思考一下 [ ] [ ] ? 答案是空字符串 为什么呢? 当做加法运算的时候,发现左右两端存在非原始类型,也就是引用类型对象,就会对对象做隐式类型转换 如何执行的?或者说怎么查找的? 第一步&…...
ubuntu20根目录扩容
ubuntu根目录/ 或者 /home文件夹有时出现空间满了的情况,可以用gparted工具进行空间的重新分配。 首先,如果你是双系统,需要从windows系统下磁盘压缩分配一部分未使用的空间给ubuntu,注意压缩的空间要邻接ubuntu所在盘的位置。 …...
(四)DQL数据查询语言
基础语法 SELECT {*,列名,函数} FROM 表名 [WHERE 条件]; 说明: -SELECT检索关键字 *匹配所有列 , 匹配指定列 -FROM 所提供的数据源(表,视图,另一个查询机制反馈的结果) -WHERE 条件(控制查询的区…...
网络安全03---Nginx 解析漏洞复现
目录 一、准备环境 二、实验开始 2.1上传压缩包并解压 2.2进入目录,开始制作镜像 2.3可能会受之前环境影响,删除即可 编辑 2.4制作成功结果 2.5我们的环境一个nginx一个php 2.6访问漏洞 2.7漏洞触发结果 2.8上传代码不存在漏洞 2.9补充&#…...
第十四届蓝桥杯C组题目 三国游戏
4965. 三国游戏 - AcWing题库 小蓝正在玩一款游戏。 游戏中魏蜀吴三个国家各自拥有一定数量的士兵 X,Y,Z(一开始可以认为都为 00)。 游戏有 n 个可能会发生的事件,每个事件之间相互独立且最多只会发生一次,当第 i个事件发生时会分…...
【LeetCode-435】无重叠区间(贪心)
题目链接 题目简介 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。 注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。 示例 1: 输入: [ [1,2], [2,3], [3,4…...
写读后感的时候,可以适当地引用书中的内容吗?
写读后感时,适当地引用书中的内容是可以的,这样可以更好地支持你的观点和感受,增强文章的可信度和说服力。 引用书中的内容可以帮助读者更好地理解你所讨论的主题和人物,同时也可以展示你对原著的深入理解和阅读能力。但是&#…...
RockChip DRM Display Driver
资料来源: 《Rockchip_DRM_Display_Driver_Development_Guide_V1.0.pdf》 《Rockchip_Developer_Guide_DRM_Display_Driver_CN.pdf》 一:DRM概述 DRM(Direct Rendering Manager)直接渲染管理,buffer分配,帧缓冲。对应userspace库位libdrm,libdrm库提供了一系列友好的…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
