STL源碼剖析 5中迭代器型別

  • ?最常使用的5種迭代器的型別 為 value_type、difference_type、pointer、reference、iterator_category。
  • 如果想要自己開發的容器和STL進行適配,就需要定義上述5種類型?
  • iteraor_traits 必須針對傳入的型別為 pointer 或者 pointer-to-const設計偏特化版本
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};

value_type

  • 迭代器所指對象的型別?

difference_type

  • 兩個迭代器之間的距離,表示容器的最大容量([begin,end))
  • 例:STL 的count() 返回的數值就是迭代器的 difference type
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};template <class I,class T>
typename iteraor_traits<I>::difference_type //這一整行限定的是函數的回返型別
count (I first,I last,const T& value){typename iteraor_traits<I>::difference_type n = 0;for (;first != last;++first) {if (*first == value){++n;}}return n;
}
  • 針對相應型別differe type,traits 的如下兩個(針對原生指針而寫的)特化版本
  • 使用C++內建的ptrdiff_t (定義在<cstddef>頭文件) 作為原生指針的difference type
//針對原生指針設計的偏特化版本
template <class T>
struct iteraor_traits<T*>{typedef ptrdiff_t difference_type;
};//針對原生的pointer-to-const設計的偏特化版本
template <class T>
struct iteraor_traits<const T*>{typedef ptrdiff_t difference_type;
};
  • 當我們需要的任何迭代器I的difference type,可以怎么寫 typename iterator_traits<I>::difference_type?

pointer

  • 指針和引用之間的關系是非常密切的,傳回一個左值,令他代表所指之物,也可以令其代表所指之物的地址,即可以通過傳回一個指針,指向迭代器的所指之物
Item& operator*() const {return *ptr;}
Item* operator->() const {return ptr;}
//針對原生指針設計的偏特化版本
template <class T>
struct iteraor_traits<T*>{typedef T* pointer;typedef T& reference;
};//針對原生的pointer-to-const設計的偏特化版本
template <class T>
struct iteraor_traits<const T*>{typedef const T* pointer;typedef const T& reference;
};

reference

  • 從迭代器所指之物的內容是否允許改變的角度出發,迭代器分為兩種:不允許改變所指對象的內容 稱為const int*pic
  • 允許改變所指之物的內容 稱為mutable iterators ,例如int * pi;
  • 對于mutable iterator進行提領操作的時候,得到的不是一個右值(右值不允許賦值操作),應該是一個左值。
    int *pi = new int(5);const int* pci = new int(9);*pi = 7; //對mutable iterator進行提領操作的時候 得到的是左值,允許賦值*pci = 1; //這個操作是不允許的,pci是const iterator//提領pci得到的結果是一個 右值 不允許賦值
  • C++左值是通過reference的方式返回的
  • 當p是mutable iterator時,其value type是T? 那么*p型別不應該是T 應該是T&
  • 當p是constant iterator時,其value type是const T? 那么*p型別不應該是const T 應該是const T&

iterator_category

//針對原生指針設計 偏特化版本
template <class T>
struct iteraor_traits<T*>{//注意 原生指針是一種Random access iteratortypedef random_access_iterator_tag iterator_category;
};//針對原生指針 pointer-to-const 設計 偏特化版本
template <class T>
struct iteraor_traits<const T*>{//注意 原生指針pointer-to-const 是一種Random access iteratortypedef random_access_iterator_tag iterator_category;
};

迭代器的分類( 根據移動特性和施行操作?)

  • Input iterator :不允許外界改變 只讀
  • output iterator:唯寫
  • forward iterator:允許寫入型算法 (例如 replace() ) 在這種迭代器形成的區間上進行讀寫操作
  • bidirectional iterator : 可以雙向移動 ,可以逆向訪問迭代器的區間(例如逆向 拷貝某個范圍內的元素)
  • random access iterator : 前四種僅僅具備一部分指針算數的能力(前三種支持operator++,第四種需要加上operator--);第五種需要涵蓋所有的只針的算數的能力,比如p+n p-n? p[n]? p1-p2 p1<p2

  • 直線和箭頭并不代表 繼承的關系,而是所謂的 概念 和強化的關系
  • 例子 使用advance函數舉例,這個函數有兩個參數,迭代器p和數值n,函數需要實現迭代器p累計前進n次
  • 3個例子:input iterator、bidirectional? iterator 、random access iterator
  • 倒是沒有針對forwarditerator設計的版本,因為這個 和 inputiterator而設計的版本完全一致
template <class InputIterator,class Distance>
void advance_II(InputIterator& i,Distance n){//單向 逐一前進while (n--)++i;
}template <class BidirectionalIterator,class Distance>
void advance_BI(BidirectionalIterator& i,Distance n){//雙向 逐一前進if(n >= 0){while (n--){++i;}} else{while (n++){--i;}}
}template <class RandomAccessIterator,class Distance>
void advance_RAI(RandomAccessIterator&i ,Distance n){//雙向 跳躍前進i += n;
}
  • ?程序調用advance()的時候 具體使用哪一個函數定義呢?如果選擇advance_II()對于randomaccess iterator而言 缺乏效率,O(1)操作變成了O(N)操作
  • 如果是advance_RAI() 則無法接受 Input Iterator? 需要對上述三個函數進行合并
template <class InputIterator,class Distance>
void advance(InputIterator& i,Distance n){if (is_random_access_iterator(i)){advance_RAI(i,n);} else if (is_bidiractional_iterator(i)){advance_BI(i,n);} else{advance_II(i,n);}
}
  • 在執行期間決定使用哪一個版本,會影響程序的執行的效率,最好在編譯期間就確定程序執行的正確的版本,因此使用函數的重載是最好的方式
struct _LIBCPP_TEMPLATE_VIS input_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS output_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS forward_iterator_tag       : public input_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS bidirectional_iterator_tag : public forward_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS random_access_iterator_tag : public bidirectional_iterator_tag {};
  • 上述這些classes只能作為標記使用,不需要任何成員
  • 加上第三個參數,讓他們之間形成重載的關系
template <class InputIterator,class Distance>
void __advance(InputIterator& i,Distance n,input_iterator_tag){//單向 逐一前進while (n--)++i;
}//這是一個單純的傳遞調用的函數(trivial forwarding function)
template <class ForwardIterator,class Distance>
inline void __advance(ForwardIterator&i, Distance n,forward_iterator_tag){//單純的進行傳遞調用 (forwarding)advance(i,n,input_iterator_tag());
}template <class BidirectionalIterator,class Distance>
void advance_BI(BidirectionalIterator& i,Distance n,bidirectional_iterator_tag){//雙向 逐一前進if(n >= 0){while (n--){++i;}} else{while (n++){--i;}}
}template <class RandomAccessIterator,class Distance>
void advance_RAI(RandomAccessIterator&i ,Distance n,random_access_iterator_tag){//雙向 跳躍前進i += n;
}
  • __advance()的第三個參數只聲明了型別,但是并沒有指定參數的名稱,其主要的目的是為了激活重載機制,函數中根本不使用這個參數
  • 還需要一個對外開放的上層控制接口,調用上述的各自重載的__advance() ,這一層只需要兩個參數,當其準備將工作轉移給上述的_advance()時才會自行加上第三個參數:迭代器的類型。
  • 使用traits機制 從所獲得的迭代器中推導出 其類型
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};template <class InputIterator,class Distance>
inline void advance(InputIterator&i, Distance n){__advance(i,n,iteraor_traits<InputIterator>::iterator_category());
}
  • iteraor_traits<InputIterator>::iterator_category() 將產生一個暫時對象,就像int() 產生一個int暫時對象一樣。
  • 這個臨時對象的型別應該隸屬于前面所述的5個迭代器之一,然后根據這個 迭代器的型別,編譯器才決定調用哪一個_advance()重載函數

?注意事項

  • 迭代器其類型隸屬于各個適配類型中最強化的那個。例如int* 既是random 、bidirectional、forward、input,那么其類型應該從屬為random_access_iterator_tag
  • 但是迭代器的參數需要按照最低階的類型為其命名,比如advance()函數,使用最低級的inputIterator為其命名

消除 “單純傳遞調用的函數”

  • 使用class定義迭代器的各種分類標簽,不僅可以促進重載機制的成功運作,使得編譯器得以正確執行重載決議,overloaded resolution
  • 還可以 通過繼承不比再寫 "單純只做傳遞調用"的函數,即advance不需要寫 Forwarditerator這個版本

  • 仿真測試tag types繼承關系所帶來的影響

  • ?例子:使用distance舉例子 計算兩個迭代器之間的距離

  • distance可以接受任何類型的迭代器,但是template型別參數設置為InputIterator,是為了遵循STL算法的命名規則,使用最低級的迭代器命名
  • 考慮到繼承關系,當客戶端調用distance()函數輸入的參數型別是 Output iterators、Bidirectional Iterators、ForwardIterator時,統統都會轉化為Input Iterator版_distance函數
  • 即存在繼承關系的模型,僅僅實現首 和 尾即可,中間版本都會被隱式轉化為首,尾巴調用專屬的函數即可

?std::iterator 保證

  • 規范需要任何的迭代器都需要實現上述的五個內嵌的型別,從而方便traits進行類型的萃取,否則無法適配 STL的組件
  • STL提供了iterator class,新設計的迭代器需要繼承自它,它不包含任何的成員,只是進行了型別的定義,因此繼承它 不會導致額外的負擔
#include <memory>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 bidirectional_iterator_tag{};//自行開發的迭代器需要繼承 std::iterator
template <class Category,class T,class Distance = ptrdiff_t,class Pointer = T*,class Reference = T&>
struct iterator{typedef Category    iterator_category;typedef T           value_type;typedef Distance    difference_type;typedef Pointer     pointer;typedef Reference   reference;
};//traits
template <class Iterator>
struct iterator_traits{typedef typename Iterator::iterator_category iterator_category;typedef typename Iterator::value_type value_type;typedef typename Iterator::difference_type difference_type;typedef typename Iterator::Pointer pointer;typedef typename Iterator::reference reference;
};//針對原生指針(native pointer)設計traits偏特化版本
template <class T>
struct iterator_traits<T*>{typedef random_access_iterator_tag iterator_category;typedef T                          value_type;typedef ptrdiff_t                  difference_type;typedef T*                         pointer;typedef T&                         reference;
};//針對原生指針(pointer-to-const)設計的traits偏特化版本
template <class T>
struct iterator_traits<const T*>{typedef random_access_iterator_tag iterator_category;typedef T                          value_type;typedef ptrdiff_t                  difference_type;typedef T*                         pointer;typedef T&                         reference;
};//函數的目的是為了 方便的決定某個迭代器的類型 (category)
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&){typedef typename iterator_traits<Iterator>::iterator_category category;return category();
}//函數的目的是為了 方便的決定某個迭代器的類型 distance type
template <class Iterator>
inline typename iterator_traits<Iterator>::difference_type *
distance_type(const Iterator&){return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}//函數的目的是為了 方便的決定某個迭代器的類型 value type
template <class Iterator>
inline typename iterator_traits<Iterator>::value_type  *
value_type(const Iterator&){return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}//整組的distance函數
template <class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
__distance(InputIterator first,InputIterator last,input_iterator_tag){typename iterator_traits<InputIterator>::difference_type n = 0;while (first != last){++first;++n;}return n;
}template <class RandomAccessIterator>
inline typename iterator_traits<RandomAccessIterator>::difference_type 
__distance(RandomAccessIterator first,RandomAccessIterator last,random_access_iterator_tag){return (last - first);
}template <class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first,InputIterator last){typedef typename iterator_traits<InputIterator>::iterator_category category;return (__distance(first,last,category()));
}//以下是整組的advance函數
template <class InputIterator,class Distance>
inline void __advance(InputIterator& i,Distance n,input_iterator_tag){while (n--){++i;}
}template <class BidirectionalIterator,class Distance>
inline void __advance(BidirectionalIterator&i,Distance n,bidirectional_iterator_tag){if (n>=0){while (n--) ++i;} else{while (n++) --i;}
}template <class RandomAccessIterator,class Distance>
inline void __advance(RandomAccessIterator& i,Distance n,random_access_iterator_tag){i+=n;
}template<class InputIterator,class Distance>
inline void advance(InputIterator& i,Distance n){__advance(i,n,iterator_category(i));
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446288.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446288.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446288.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python學習16 正則表達式3 練習題

用戶名匹配 1.用戶名匹配&#xff1a;由數字、大小寫字母、下劃線_、中橫線-組成&#xff0c;長度為6-12位&#xff0c;不能以數字開頭。 import re usernameab578_-SDF resultre.search(^[a-zA-Z_-][0-9a-zA-Z_-]{5,12}$,username) print(result)郵箱 2.驗證輸入的郵箱&…

加載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負責萃…

java 學生成績

題目 對學生成績大于60分的&#xff0c;輸出“合格”。低于60分的&#xff0c;輸出“不合格” 代碼 使用/除法簡化代碼 package l1_switch_case;import java.util.Scanner;public class SwitchDemo2 {public static void main(String[] args) {Scanner scanner new Scanne…

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;就像我們用其它語言…

java 判斷日期為第幾天

題目1 編寫程序&#xff1a;從鍵盤上輸入2019年的“month”和“day”&#xff0c;要求通過程序 輸出輸入的日期為2019年的第幾天。 代碼1 從12月往下加日期數 package l1_switch_case; import java.util.Scanner; public class SwitchDemo4 {public static void main(Strin…

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…

java 年份對應的中國生肖

題目 編寫一個程序&#xff0c;為一個給定的年份找出其對應的中國生肖。 中國的生肖基于12年一個周期&#xff0c; 每年用一個動物代表&#xff1a; rat、ox、tiger、rabbit、dragon、snake、horse、sheep、monkey、 rooster、dog、pig。 提示&#xff1a;2019年&#xff1a;豬…

密碼學專題 對稱加密算法

一般來說&#xff0c;使用OpenSSL對稱加密算法有兩種方式&#xff0c;一種是使用API函數的方式&#xff0c;一種是使用OpenSSL提供的對稱加密算法指令方式。本書將介紹對稱加密算法的指令方式OpenSSL的對稱加密算法指令主要用來對數據進行加密和解密處理&#xff0c;輸入輸出的…

網絡防火墻單向和雙向_單向晶閘管與雙向晶閘管之間的不同之處

晶閘管是回一個可以控導點開關&#xff0c;能以弱電去控制強電的各種電路。晶閘管常用于整流&#xff0c;調壓&#xff0c;交直流變化&#xff0c;開關&#xff0c;調光等控制電路中。具有提交小&#xff0c;重量輕&#xff0c;耐壓高&#xff0c;容量大&#xff0c;效率高&…

java 遍歷100以內的偶數,偶數的和,偶數的個數

題目 遍歷100以內的偶數&#xff0c;偶數的和&#xff0c;偶數的個數 代碼 package l2_for; /*遍歷100以內的偶數&#xff0c;偶數的和&#xff0c;偶數的個數*/ public class ForDemo1 {public static void main(String[] args) {//方法1&#xff1a;int sum1 0,count10;f…

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;配置…

java 打印

題目 編寫程序從1循環到150&#xff0c;并在每行打印一個值&#xff0c;另外在每個3的倍數行 上打印出“foo”,在每個5的倍數行上打印“biz”,在每個7的倍數行上打印 輸出“baz”。 代碼 package l2_for;/** 編寫程序從1循環到150&#xff0c;并在每行打印一個值&#xff0c…

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)的概念&…

java 最大公約數和最小公倍數

題目 題目&#xff1a;輸入兩個正整數m和n&#xff0c;求其最大公約數和最小公倍數。 比如&#xff1a;12和20的最大公約數是4&#xff0c;最小公倍數是60。 說明&#xff1a;break關鍵字的使用 代碼一 package l2_for; //題目&#xff1a;輸入兩個正整數m和n&#xff0c;求…