STL源码剖析 迭代器的概念和traits编程技法

  • 迭代器:依序巡防某个聚合物(容器)所含的各个元素,但是不需要暴露这个聚合物的内部表述方式
  • 核心思想:将容器和算法分开,彼此独立设计
  • 容器和算法的泛型化,均可以使用模板,使用迭代器连接容器和算法
  • 例子
template <class InputIterator,class T>
InputIterator find(InputIterator first,InputIterator last,const T& value){while (first != last && *first != value){++first;}return first;
}
  • 迭代器 是一种类似智能指针的对象,最为关键的操作是内容提领和成员访问。
  • 因此最关键的操作是对operator* 和 operator->进行重载工作
  • 参考 智能指针auto_ptr  (参考思想)
void func(){auto_ptr<std::string>ps(new std::string("input_string"));std::cout << *ps << std::endl;        //输出input_stringstd::cout << ps->size() << std::endl; //输出 5//离开之前不需要使用delete进行资源的释放 auto_ptr自动释放内存
}
  • 使用new动态配置一个初始值为 input_string 的string对象,并将所得到的一个结果(原生指针) 作为auto_ptr<std::string>对象的初始数值 
  • auto_ptr里面放的是原生指针所指对象的型别,而不是原生指针的型别
  • auto_ptr 的简单实现
template<class T>
class auto_ptr{
public:explicit auto_ptr(T* p = 0):pointee(p){}template<class U>auto_ptr(auto_ptr<U>& rhs):pointee(rhs.release()){}~auto_ptr(){delete pointee;}template <class U>auto_ptr<T>& operator=(auto_ptr<U>& rhs){if (this != rhs)reset(rhs.release());return *this;}T& operator*() const {return *pointee;}T& operator->() const {return pointee;}T* get() const {return pointee;}private:T* pointee;
};
  • 迭代器会暴露太多的细节,因此将迭代器交给了每一个专属的 容器,这也就是为啥每一个容器都有自己的专属迭代器的缘故
  • 如何判断迭代器所指对象的型别?使用函数模板的参数推导机制

  •  func()为对外的接口,实际操作是由func_impl()实现的
  • func_impl是一个函数模板 一旦被调用 编译器会自动进行模板的参数推导,推导出型别T 
  • 迭代器所指对象的型别,称为该迭代器的value type,虽然上面的参数型别的推导机制可以适用于value type,但是并非是通用的方法,如果value type必须用于函数的返回值,就无法使用template的参数推导机制,毕竟 这个只适用于参数的推导 ,不可以推导 函数的返回值
  • 解决上面问题的办法  声明内嵌型别 

  • func()返回的型别必须加上关键词typename,因为T是一个template的参数,在编译器对其具现化之前,编译器不知道T是什么东西,也就是编译器无法知道MyIter<T>::value_type是什么类型,其代表的是一个型别?还是一个成员函数?还是数据成员?
  • 关键词typename相当于告诉编译器 这是一个型别,才可以顺利通过编译
  • 但是并不是所有的迭代器都是class type,原生指针就不是,如果不是class type是不可以为其定义内嵌型别的。但是STL容器必须接受原始指针作为一种迭代器,上述的办法还是不足以解决这个问题,那么如何实现对于上述一般化概念针对特殊情况(例如 针对原生指针)进行特殊化处理呢?
  • 使用template partial specialization就可以实现 (偏特化)
  • 偏特化的含义:如果class template拥有一个以上的template参数,可以针对其中某个(或者数个,但不是全部)的template参数进行特殊化处理,即可以在泛化版本中提供一个特殊化的版本,也就是将泛化版本中的某些template参数赋予明确的指定
template <typename U,typename V,typename T>
class C{};
  • 偏特化很容易让我们陷入一种误区,比如偏特化一定是对template参数U或者V或者T或者某种组合指定某个参数值,这是不对的
  • 偏特化是提供另外一份template 定义式,但是本身仍然是templatized;针对任何template参数更进一步的条件限制所设计出来的一种特殊化版本
  • 例:这个泛型版本允许接受T为任何型别
template <typename T>
class C{};
  • 很容易接受他有如下形式的 偏特化设计
  • 这个特殊化版本 仅仅适用于“T为原始指针”的情形,T为原生指针是相较于 T为任何型别的更进一步的限制
template <typename T>
class C<T*>{ };
  • 解决内嵌型别未解决的问题,也就是 原生指针不是class,因此无法为其定义内嵌型别,因此可以针对 “迭代器之template参数为指针”设计特化版的迭代器
  • 这个class template专门用于 萃取 迭代器的特性,value_type正是迭代器特性之一
template <class I>
struct iteraor_traits{ //traits的含义是 "特性"typedef typename I::value_type value_type;
};
  • 这个traits的含义是 如果 I 定义有自己的value type通过这个traits就可以萃取得到的value_type就是I:: value_type,如果I定义有自己的value type 先前的func()可以进一步的改下成如下形式
template <class I>
typename iteraor_traits<I>::value_type //这一整行是函数的返回型别func(I ite){return *ite;}
  • 这新增的一层间接性,可以带来的好处是什么呢?可以为traits 进行偏特化的设计
  • 令iterator_traits 拥有的一个偏特化的设计如下
template <class T>
struct iteraor_traits<T*>{ //偏特化版本 迭代器是一个原生指针typedef T value_type;};
  • 于是 原生指针int* 虽然不是一种class type 但是仍然可以通过traits取到value type
  • 注意:针对“指向常数对象的指针(pointer to const)” iterator_traits<const int *>::value_type
  • 上述得到的结果是const int,而不是int,原本是希望声明一个暂时的变量,使得其类型和迭代器的类型value_type一致,但是现在被const修饰无法赋值,不满足需求,因此当迭代器是一个 pointer to const的类型,只能另外设计一个偏特化版本,让value type指向的是non const版本即可
template <class T>
struct iterator_traits<const T*>{//偏特化版本 当迭代器的类型是pointer-to-const的时候//萃取出来的型别是 T 而不是const Ttypedef T value_type;
};
  • 这样的话 不管面对的是迭代器、原生指针int* 或者const int* 都可以通过traits 取出正确的 value type
  • 使用traits 萃取各个迭代器的特性,这里特性是指 迭代器的相应的型别,但是需要每一个迭代器都需要使用内嵌型别定义的方式定义出相应的型别

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STL源码剖析 5中迭代器型别

最常使用的5种迭代器的型别 为 value_type、difference_type、pointer、reference、iterator_category。如果想要自己开发的容器和STL进行适配&#xff0c;就需要定义上述5种类型 iteraor_traits 必须针对传入的型别为 pointer 或者 pointer-to-const设计偏特化版本 template &…

加载tf模型 正确率很低_深度学习模型训练全流程!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;黄星源、奉现&#xff0c;Datawhale优秀学习者本文从构建数据验证集、模型训练、模型加载和模型调参四个部分对深度学习中模型训练的全流程进行讲解。一个成…

Python学习17 Turtle库绘图

学习网址&#xff1a;https://docs.python.org/zh-cn/3/library/turtle.html Turtle库 Turtle库是Python语言中一个很流行的绘制图像的函数库&#xff0c;一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff08;画布中心&#xff09;&#xff0c;(0,0)位置开…

android ros 节点编写_嵌入式的我们为什么要学ROS

前言本来是要写一篇STM32移植ROS的一个小lib库&#xff0c;ROS一般都是需要跑在Linux上的&#xff0c;STM32使用就是当成一个ROS通讯的小节点&#xff0c;但是写文章时间不够&#xff0c;所以就简单做一篇ROS的介绍文章&#xff0c;分享给嵌入式的小伙伴们。ROS现在在机器人领域…

STL源码剖析 __type_traits

traits编程 弥补了C本身的不足STL只对迭代器进行规范制定出了iterator_traits&#xff0c;SGI在此基础上进一步扩展&#xff0c;产生了__type_traits双下划线的含义是这个是SGI内部使用的东西&#xff0c;不属于STL标准iterator_traits 负责萃取迭代器的特性__type_traits负责萃…

STL源码剖析 序列式容器|Vector

容器的概观和分类 array 数组 、list 链表、tree树 、stack堆栈、queue队列、hash table散列表、set集合、map映射表根据数据在容器中的排列顺序&#xff0c;将上述数据结构分为序列式和关联式两种类型SGI STL使用内缩方式来表达基层和衍生层之间的关系衍生不是派生&#xff0…

ansible 修改文件变量_Ansible Playbook中的变量与引用

Ansible是一个系列文章&#xff0c;我会尽量以通俗易懂、诙谐幽默的总结方式给大家呈现这些枯燥的知识点&#xff0c;让学习变的有趣一些。Ansible自动化运维前言前面有说到使用playbook来搞一些复杂的功能&#xff0c;我们使用YAML来写playbook&#xff0c;就像我们用其它语言…

STL源码剖析 list概述

目录 list的节点(node) list迭代器 list 的构造和内存管理 list 的元素操作 list相较于vector连续的线性空间就显得很复杂&#xff0c;他的存储空间是不连续的&#xff0c;好处是每次插入和删除一个元素的时候&#xff0c;只需要配置或者释放一个元素的空间 插入和删除十分的…

vsftp不允许切换到其它目录_IntelliJ IDEA如何对project的目录进行筛选显示?

如果你的项目很庞大&#xff0c;同一个功能用到的各种文件散落在多个文件夹&#xff0c;开发时切换不便&#xff0c;可以利用scope功能&#xff0c;只显示该功能用到的文件&#xff0c;让project列表十分清爽&#xff0c;提高开发效率。本文使用的IDEA版本为2020.1。1、打开sco…

密码学专题 对称加密算法

一般来说&#xff0c;使用OpenSSL对称加密算法有两种方式&#xff0c;一种是使用API函数的方式&#xff0c;一种是使用OpenSSL提供的对称加密算法指令方式。本书将介绍对称加密算法的指令方式OpenSSL的对称加密算法指令主要用来对数据进行加密和解密处理&#xff0c;输入输出的…

网络防火墙单向和双向_单向晶闸管与双向晶闸管之间的不同之处

晶闸管是回一个可以控导点开关&#xff0c;能以弱电去控制强电的各种电路。晶闸管常用于整流&#xff0c;调压&#xff0c;交直流变化&#xff0c;开关&#xff0c;调光等控制电路中。具有提交小&#xff0c;重量轻&#xff0c;耐压高&#xff0c;容量大&#xff0c;效率高&…

python版本切换_怎么切换python版本

展开全部 &#xff08;1&#xff09;分别安2113装 python-2.7.12.amd64.msi python-3.5.2-amd64.exe &#xff08;python官网下载的&#xff09; 顺序无所谓&#xff08;为5261了看着4102方便&#xff0c;我把安装路径修改统一了1653&#xff09; &#xff08;2&#xff09;配置…

react.lazy 路由懒加载_Vue面试题: 如何实现路由懒加载?

非懒加载import List from /components/list.vue const router new VueRouter({routes: [{ path: /list, component: List }] })方案一(常用)const List () > import(/components/list.vue) const router new VueRouter({routes: [{ path: /list, component: List }] })方…

STL源码剖析 deque双端队列 概述

vector是单向开口的连续线性空间&#xff0c;deque是一种双向开口的连续线性空间。deque可以在头尾两端分别进行元素的插入和删除操作vector和deque的差异 1&#xff0c;deque允许常数时间内对于头端元素进行插入和删除操作2&#xff0c;deque没有所谓容量(capacity)的概念&…

STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

Stack是一种先进后出的数据结构&#xff0c;他只有一个出口stack允许 新增元素、移除元素、取得最顶端的元素&#xff0c;但是无法获得stack的内部数据&#xff0c;因此satck没有遍历行为Stack定义的完整列表 (双端队列作为Stack的底层容器) 将deque作为Stack的底部结构&#…

python怎么实现图像去噪_基于深度卷积神经网络和跳跃连接的图像去噪和超分辨...

Image Restoration Using Very Deep Convolutional Encoder-Decoder Networks with Symmetric Skip Connections作者&#xff1a;Xiao-Jiao Mao、Chunhua Shen等本文提出了一个深度的全卷积编码-解码框架来解决去噪和超分辨之类的图像修复问题。网络由多层的卷积和反卷积组成&a…

STL源码剖析 queue队列概述

queue是一种先进先出的数据结构&#xff0c;他有两个出口允许新增元素&#xff08;从最底端 加入元素&#xff09;、移除元素&#xff08;从最顶端删除元素&#xff09;&#xff0c;除了对于顶端和底端元素进行操作之外&#xff0c;没有办法可以获取queue的其他元素即queue没有…

python为什么运行不了_python为什么会环境变量设置不成功

学习python编程&#xff0c;首先要配置好环境变量。本文主要讲解python的环境变量配置&#xff0c;在不同版本下如何安装 Windows 打开Python官方下载网站 x86:表示是32位电脑 x86-64:表示是64位电脑 目前Python版本分为2.x版本和3.x版本。推荐大家使用3.x版本。 设置环境变量&…

STL 源码剖析 heap堆

heap不属于STL容器的组件&#xff0c;属于幕后角色&#xff0c;是priority_queue的助手priority_queue 允许用户以任何次序将任何元素推入容器内&#xff0c;但是取出的时候需要从优先级最高(也就是数值最高)的元素开始取&#xff0c;这种思想是基于heap的函数实现如果使用list…

java 打印星号

代码1 package lesson.l2_for; //6列4行 //****** //****** //****** //****** public class ForDemo8 {public static void main(String[] args) {for (int i1;i<4;i){for (int j 1; j <6 ; j) {System.out.print("*");}System.out.println();}} }代码2 pa…