1. 程式人生 > >C++中的萃取原理

C++中的萃取原理

 萃取(traits

    它主要解決的問題是相應型別(associated type,迭代器所指之物的型別便是其一。若要以“迭代器所指物件的型別”來宣告一個物件,如果如果沒有traits ,解決辦法是:利用function

template的引數推導(argument deducation)機制。

如圖:

我們以func()為對外介面,卻把實際操作全部置於func_impl()之中,由於func_impl()是一個function template,一旦被呼叫,編譯器會自動進行template引數推導。預算匯出型別T,順利解決問題。但是迭代器相應型別有五種,然而並非任何情況下任何一種都可利用上述的

template引數推導機制來取得,但是我們往往需要更全面的解法。

  上述方法的缺陷是萬一 value type 必須用於函式的返回值,就束手無策了,畢竟函式的“tmplate引數推導機制”推而導之的只是引數,無法推導函式的返回值型別。

我們需要其他方法。宣告內嵌型別似乎是一個好主意,像這樣:

template<class T>
struct MyIter
{	typedef T value_type;
	T*ptr;
	MyIter(T *p= 0):ptr(p){}
	T& operator*()const {return *ptr;}
};

template<class T>
typename T::value_type
	func(T ite){return *ite;}


void main()
{
	MyIter<int> ite(new int(8));
	cout<<func(ite);
}

在此列子中,有個隱晦的陷阱:並不是所有的迭代器都是class type。原生指標就不是!

如果不是class type,就無法為它定義內嵌型別。但STL(以及整個泛型思維)絕對接收原生指標作為一種迭代器,所以上面這樣還不夠。有沒有辦法可以讓上述的一般化概念針對特定情況(例如針對原生指標)做特殊處理呢?template partial specialization可以做到。

Partial Specialization(偏特化)的意義:

如果class template 擁有一個以上的template引數,我們可以針對其中某個(或數個,但非全部)template引數進行特化工作。換句話說,我們可以在泛華設計中特工一個特化版本(也就是將泛化版本中的某些

template引數賦予明確的指定)。

    有了這項利器,我們便可以解決前述“內嵌型別”未能解決的問題。先前的問題是,原生指標並非class,因此無法為它們定義內嵌型別。現在,我們可以針對“迭代器之template引數為指標”者,設計特別版的迭代器。

下面這個class template專門用來“萃取”迭代器的特性,而value type正是迭代器的特性之一:

#include<iostream>
using namespace std;
template<class T>
struct	iterator_traits//traits 意為“特性”
{
	typedef typename I::vlue_type value_type;
};

這個所謂的traits,其意義是,如果I定義自己的value type,那麼通過這個traits的作用,萃取出來的value_type就是I::value_type。換句話說,若果I 定義有自己的value type ,先那個func()可以改寫成這樣:

template <class T>
typename iteraotr_traits<T>::value_type//這一整行是函式返回值
	func(T ite)
{return *ite;}

但這除了多了一層間接性,好處是traits可以擁有特化版本。現在,我們令iterator_traites擁有一個partial specializations如下:

template<class>

struct iterator_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相同,而現在,宣告一個無法複製的暫時變數,沒什麼用!因此,如果迭代器是一個pointer-to-const,我們應該設法令其value type為一個non-const型別。只需要另外設計一個特化版本就可以解決問題:

template<class T>
struct iterator_traits<const T*>{//偏特化版—當迭代器是一個pointer-to-const
typedef T value_type;//萃取出來的型別應該是T,而非const T
};

現在,不論面對的是迭代器MyIter,或是原生指標*int 或const int*,都可以通過traits取出正確的(我們所期望的)value type。

下圖說明了traits所扮演的“特性萃取機”角色,萃取各個迭代器的特性。這裡所謂的迭代器特性,指的是迭代器的相應型別。當然,若要這個“特性萃取機”traits嫩夠有效運作,每一個迭代器必須遵守約定,自行以內嵌型別定義的方式定義出相應型別。這是一個約定,誰不遵守約定,誰就不能相容STL這個大家庭。

摘自《STL原始碼剖析》。


相關推薦

C++原理

 萃取(traits)     它主要解決的問題是相應型別(associated type),迭代器所指之物的型別便是其一。若要以“迭代器所指物件的型別”來宣告一個物件,如果如果沒有traits ,解決辦法是:利用function template的引數推導(argumen

C#foreach實現原理

示例 元素 res 過程 false 編程語言 static posit this 本文主要記錄我在學習C#中foreach遍歷原理的心得體會。 對集合中的要素進行遍歷是所有編碼中經常涉及到的操作,因此大部分編程語言都把此過程寫進了語法中,比如C#中的foreach。經

探究Objective-C關聯物件原理

一、實際問題 1.提出問題 首先,一切都要從一個問題開始:在Objective-C中,能否在Category中為類新增屬性及對應的例項變數? 該題的答案是:不能。 2.分析解答 為什麼不能通過Category來為Objective-C的類新增屬性及對應的例項變數

C#的反射原理

 反射的定義:審查元資料並收集關於它的型別資訊的能力,元資料(編輯後的基本資料單元)就是一大堆表,編譯器會建立一個類定義表,一個欄位定義表,一個方法定義表等,System.Reflection名稱空間包含的幾個類,允許你反射(解析)這些元資料的程式碼一、反射的作用:動態的建

C++之技術

自從C++中引入了template後,以泛型技術為中心的設計得到了長足的進步。STL就是這個階段傑出的產物。STL的目標就是要把資料和演算法分開,分別對其進行設計,之後通過一種名為iterator的東西,把這二者再粘接到一起。設計模式中,關於iterator的描述為:一種能夠

C/C++地址與引用

取地址和引用都採用&的識別符號,很容易讓人造成誤會。 就c/C++而言。引用只是C++中的知識,而取地址是兩者共有的。 取地址: 在第6行中我定義了一個指向int型別的指標p,運用&a獲取a的地址,並將a的地址賦予它,即p指向a所在的空間。 在第7行中取p

C#的反射原理及應用

反射的概述 反射的定義:審查元資料並收集關於它的型別資訊的能力。元資料(編譯以後的最基本資料單元)就是一大堆的表,當編譯程式集或者模組時,編譯器會建立一個類定義表,一個欄位定義表,和一個方法定義表等,。System.reflection名稱空間包含的幾個類,允許你反射(

C#的事件原理

看了幾遍,終於有所得,根據從163上下載來的視訊,應該是這樣的     class Publisher    {        public delegate void Publish();   //1、定義一個委託,也就是事件接收者處理接收到該事件時的統一介面       

C#List指定List元素

例如跳過List前50條,然後取100條,可寫為: iclist_temp、iclist都為List型別 iclist_temp = iclist.Skip(50).Take(100).ToList(); 取前100條,可以寫為: iclist_temp = i

C++的特化問題和型別問題

模板的特化 概念  從字面上來解釋,就是為已有的模板引數進行一些使其特殊化的指定,使得以前不受任何約束的模板引數,或受到特定的修飾(例如const或者搖身一變成為了指標,甚至是經過別的模板類包裝之後的模板型別)或完全被指定了下來。 全特化

C++機制(traits)

由來 在設計迭代器(iterator)時,考慮到需要經常訪問迭代器所指物件之型別,稱之為該迭代器的value_type,利用內嵌型別宣告typedef可輕鬆實現隱藏所指物件型別: template&

C語言構造隨機數原理及rand()餘構造隨機數方法

    在C語言中,ANSIC C程式庫提供rand()函式來產生隨機數。但事實上,rand()是並不是一個真正的隨機數產生器,即可以預測隨機序列的順序,在預設隨機種子情況下產生0~99之間的隨機數,其隨機序列為{83,86,77,15,……},比如以下程式: #inclu

C#的各種json

tsl sof 添加 value default pan num 引用 com 1、添加引用Newtonsoft.Json.dll(附件:https://files.cnblogs.com/files/chen-yuan/Newtonsoft.zip); 2、引用: u

C#float的值範圍和精度分析

windows系統 weight 3.5 ans adding 發生 mage 深入 wid 本文實例分析了C#中float的取值範圍和精度。分享給大家供大家參考。具體分析如下: float類型的表現形式: 默認情況下,賦值運算符右側的實數被視為 double。 因此

C#整,向上,向下

log () ceil 取整 mat math 示例 floor 向下取整 Math.Ceiling()向上取整,Math.Floor()向下取整 示例: d = 4.56789 string res = Math.Ceiling(Convert.ToDecimal(d))

C++基類虛析構函數的作用及其原理分析

art 收回 顯示 就是 靜態綁定 運行 style 轉載 調用父類 虛析構函數的理論前提是 執行完子類的析構函數,那麽父類的虛構函數必然會被執行。 那麽當用delete釋放一個父類指針所實例化的子類對象時,如果沒有定義虛析構函數,那麽將只會調用父類的析構函數,而不會調用子

C#字符串的截

兩個 pre ext string response pla sub ces 字符串 string str="123abcd456";int i=3;1 取字符串的前i個字符 str=str.Substring(0,i); // or str=str.Remove(i

C語言小結--三個數的中間數

今天遇到一個問題,求三個數中的中間數。 我的思路是:先求兩個數中的最小數,然後把這個最小數和另一個數求最大數,這樣就取出了中間數。 求最大數和最小數的巨集定義如下: #define MAX(a, b) (((a) > (b) ) ? (a) : (b)) #defi

C++ 模板型別技術 traits

當函式,類或者一些封裝的通用演算法中的某些部分會因為資料型別不同而導致處理或邏輯不同(而我們又不希望因為資料型別的差異而修改演算法本身的封裝時),traits會是一種很好的解決方案。(型別測試發生在編譯期) 自從C++中引入了template後,以泛型技術為中心的設計得到了長足的進步。STL就是

C++指標和引用的區別、以及引用和地址符&的區別

一. 指標和引用的區別 對於指標來說,它是一個地址,這個地址是一個數值,那麼就意味這個數值可以為0(空指標),也可以為其他,即指標可以不指向任何東西。 而對於引用來說,他是一個外號,外號一定是“某個存在物體”的外號,所以引用不能為空,即不能存在空引用。例如我們給小明起了個外號:明明,那我們說