STL 中traits 技術的應用

traits

前言

都說traits 是STL源碼的鑰匙,今天看來完了這一章節,確實強大,我先講其實現的內部原理。在我看懂了traits之後,我就想,既然traits如此的強大,那我就以使用這的角度來領略它的美好,順便加強它的理解,還是先淺談traits的一些原理

爲了取得iterator 的類型,在STL可花費了大量的精力:

迭代器的類型:

第一種獲取迭代器的類型之法:利用模板函數參數推導機制:

看書中的例子:

//main函數中如此定義
int main()
{
    int i;
    func(&i); //這裏傳的是地址
}

//調用了func函數,我們來看看這個函數是怎樣定義的

void func(I iter)
{
   func_impl(iter,*iter); 
//哦,又調用了另外一個函數,有兩個參數,第一個是原來的參數 I類型,第二個是 *I 的類型,也就是*(&i) = i,恢復 了原來的對象類型
}//好的再看看func_impl到底有何玄機														
template<class I, class T>
void fucn_impl(I iter, T t)
{
T tmp ;// 呵呵,T的類型那就是i的類型,那麼在不知道i類型的情況下通過兩巧妙的調用很好的利用了函數模板的參數推到機制
}




好的,很完美的推出了我們未知對象的類型,但是,如果我要的對象類型表示函數的返回值,那麼這個方法就不可行了,因爲我們是在間接調用了兩個函數,而且最後在一個函數體的裏邊取得的類型

顯然用於修飾函數的返回值不合適  ,這個辦法不通用了,怎麼辦呢,書中的第二辦法是聲明內嵌類型

聲明內嵌類型:

比如說:


</pre><pre name="code" class="cpp">template <class T>
struct MyIter
{
    typedef T value_type;
	//這句代碼就可以通過value_type 來去的 T 這個類型了(如果對於這句代碼的作用不太理解的話,
	//可以看看我之前的那篇關於typedef的實驗博客 <a target=_blank href="http://blog.csdn.net/dalleny/article/details/39016201">http://blog.csdn.net/dalleny/article/details/39016201</a>,
	//調用的時候可以這樣寫 MyIter::value_type ,那麼在不知道T 的情況下
	//就可以取得T的類型 修飾定義變量就可以 MyIter::value_type  a;
	// 修飾函數返回值就有typename MyIter::value_type   fun(){return XX;}  ,
	//typename 是關鍵字,表示後面的MyIter::value_type 是一個類型,而不是成員變量,只有類型才能修飾返回值
	T *ptr ; //指針類型
	MyIter(T * p = 0):ptr(p){}//構造函數,初始化變量
	ptr T & operator*() const{return *ptr;} //重載運算符 *,達到解引用的目的 
	//其他工作
}


貌似挺完美了,其實不然,因爲我們的一切操作都是建立在迭代器的類型都是class type (都是類的形式),如果迭代器不是class type 比如說 原生指針,class 都沒有了,何來 member ,那麼typedef T value_type 這句話就無處可寫了

爲了使得上面的做法變得通用可行,所有指針均可適用,特偏化(特化)可以做到,(特偏化的最簡單解釋就是對template的參數進行更進一步的限制)

template <typename T>
class C{ };
//特化後,限制多一點,有點像容器vector<int>

template <typename T>
class C<T*> { }  //T可以是任何類型



這樣的話,即使原生指針不是class 類型,也不怕

通過作爲一個模板的參數 傳進去,就可以實現聲明內嵌類型

榨汁機出場啦

template<typename T>
struct iterator_traits
{
	typedef typename I::value_type  value_type 
	 //通過iterator_traits ,可以輕鬆榨出T 迭代器的類型    
	//(這裏可以看出每個迭代器必須有一個value_type,儼然已成爲標準
	//你不這樣寫,就不兼容,進不了stl大家庭)
};



特化版本

template<typename T>
struct iterator_traits<I>
{
	typedef typename I::value_type  value_type  ; 
}




所有的迭代器均可通過iterator_traits取得相應的類型

恩,除了value_type ,迭代器通常還有其他的特性可供iterator榨取, 

五種迭代器的內部類型定義

1.value type  迭代器所指向的對象的類型 比如說 vector<int >::iterator  it;   那麼 value type 的值爲 int  

2.difference type 可以表示兩個迭代器之間的距離    ,比如說,可以用這種類型來計數, 如,typename iterator_traits<I>::difference_type n =0, for(I first ; first != end; ++first )  n++;   //n這種變量可做計數,算容量

3.reference type 引用類型,

4.pointer type 指針類型

5.iterator _category 迭代器本身的類型  (詳情看下一小節),這個內部類型可以用於標識本身的類型,可用函數重載時候的版本識別, 所有重載時候的版本識別:主要是由於迭代器有5大類,無疑random_access_iterator 的功能是最強大的,但是並不是每次都是用最強大的一個版本性能就是最高的,由於其功能強大了,無疑內部實現會複雜一點,既有可能性能會有所下降,不過相當多的迭代器都是Random Access iterator 這種類型的,如果一個算法find()能夠接受一個input_iterator,那麼給它Random Access iterator  未必最好,因此,可以實現接受五個版本 的迭代器類型 find()函數,按需選擇,(注意不要使用動態綁定,這樣會大大減低性能),可以在算法中find(Iterator  ,  iterator  tag),加入一個用於識別的參數,然後通過,iterator_traits<Iterator >::category萃取出來這個category類型,可以實現函數版本識別



上面說的5中是迭代器內部預定義的內部類型,可以方便我們使用,最後一個類型表示的是迭代器本身的類型:好!下面說它

五種迭代器的類型

input iterator:只讀
output iterator 只寫
forward iterator 單向,讀寫,只能向前++  多繼承於 input iterator,output iterator 
bidirectional iterator 雙向讀寫 可++  - - 繼承於 forward iterator
Random Access iterator  隨即讀  可 +n    -n  操作寫繼承於 bidirectional iterator


個人小例子測試

#include<iostream>
#include<vector>
#include<list>
#include<deque>
#include<iterator>

using namespace std;
//下面定義5個版本的func函數,爲了簡單起見,就一個參數,分別表示不同的迭代器類型
void func(random_access_iterator_tag)
{
	cout<<"random_access_terator 類型的迭代器"<<endl;
}

void func(bidirectional_iterator_tag)
{
	cout<<"bidirectional_terator 類型的迭代器"<<endl;
}

void func(forward_iterator_tag)
{
	cout<<"forward_terator 類型的迭代器"<<endl;
}

void func(input_iterator_tag)
{
	cout<<"input_terator 類型的迭代器"<<endl;
}
void func(output_iterator_tag)
{
	cout<<"output_terator 類型的迭代器"<<endl;
}
//這裏提供一個接口給用戶使用fun()
 template<class InputIterator >

void  fun(InputIterator it)
{/這句代碼最關鍵,利用iterator_traits<IputIterator>萃取出  iterator_category屬性
    typedef iterator_traits<InputIterator>::iterator_category category;  
    func(category()); //調用去萃取
}

 int main()
 {	 
	 vector<int> ve ;
	 for(int i = 0;i<10;i++)
		 ve.push_back(i);
	
	 int cc=5;
	 int *d = &cc;  //第一個迭代器,指向int 類型
          //第二個迭代器
	 vector<int>::iterator a = ve.begin();
          //第三個迭代器
	 std::istream_iterator<int> is ;
         /分別對其調用fun函數
	 fun(d);
	 fun(a);
	 fun(is);


	 system("pause");
	 return 0;
 }



結果如下


經測試,traits 發揮功效


SGI STL私房菜 _type_traits

這是STL神器,通過 iterator_traits這個強大的武器,我們可以榨取出iterator的5種特性,還幫助我們定義相應的類型

而_type_traits 這個神器一出,那就牛逼了,可以判斷幾乎所有的類型的特性,判斷它們的五種特性

1.是否含有 trivial_default_constructor

2.是否含有 trivial_copy_constructor

3.是否含有 trivial_assignment_operator

4.是否含有trivial_destructor

5.是否爲POD類型

trivial 是沒有價值的意思,如果前4個函數返回 _true_traits (相應的有_false_traits)證明它們的那些構造析構函數,或者拷貝運算符都是無價值的,沒有必要調用,直接可以使用簡單的memcopy()函數 或者memmove()函數,這樣效率大大提高,STL的哲學就是高性能,高效率!!!

第5個所謂的POD 就是plain old data,舊風格數據,也就是C風格的數據,可以理解爲只有成員變量沒有函數和指針的struct,因此,直接可以進行成員的複製操作,無需調用什麼構造函數  (我做一個大膽的斷定,前面四個都返回_true_traits,那麼第5個必定返回_true_traits)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章