iterator模式定義如下:提供一種方法茧彤,使之能夠依序?qū)ぴL某個聚合物所含的各個元素椅寺,而又無需暴露該聚合物的內(nèi)部表達形式
其中施籍,c++里面各個容器的iterator扮演著將數(shù)據(jù)容器與算法結(jié)合起來的重要角色
將范型算法(find, count, find_if)用于某個容器中,最重要的是要給算法提供一個訪問容器元素的工具,iterator
就扮演著這個重要的角色
我們在算法中可能會定義簡單的中間變量或者設(shè)定算法的返回變量類型不铆,這時候需要知道迭代器所指元素的類型是什么,但是由于沒有typeof
這類判斷類型的函數(shù),我們無法直接獲取裹唆,那該如何是好誓斥?
不要急,那首先先介紹一下iterator_tarit
iterator_trait
template<class _Tp>
struct iterator_traits<_Tp*>
{
typedef ptrdiff_t difference_type;
typedef typename _Tp::value_type value_type;
typedef typename _Tp::pointer pointer;
typedef typename _Tp::reference reference;
typedef typename _Tp::iterator_category iterator_category;
};
看到這個奇奇怪怪的東西许帐,是不是感覺沒什么用劳坑,嗯,沒關(guān)系成畦,先記著
下面距芬,將接著之前的話題涝开,來看看如何提取出iterator
所指向的元素類型
value_type
例如
使用typedef
我們可以在迭代器中添加元素的類型
template <class T>
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template <class I>
typename I::value_type //取出迭代器類中的類型
//用以設(shè)定返回變量類型,但是如果I是指針就會錯誤
get (I ite) {
return *ite;
}
但是框仔,這個版本并不支持原生指針舀武,然而就迭代器的行為而言,就是面向容器的指針离斩,而正常的STL算法也是支持原生指針的银舱,就如同下面的find一樣
指針和迭代器的作用無非就是為stl算法提供了一個運算范圍以及對容器(無論是vector,list捐腿,亦或是array)的訪問
int main() {
int a[5] = {1,2,2,2,2};
int *begin = a;
int *end = a+5;
int count = std::count(begin, end, 2); //ok!
return 0;
}
所以對于第一個版本,我們還要對指針類型進行模版偏特化
提取以及偏特化
前面也提到了柿顶,如果直接使用typename I::value_type
茄袖,算法就無法接收原生指針,因為原生指針根本就沒有value_type
這個內(nèi)嵌類型
因此嘁锯,我們還需要加入一個中間層對其進行判斷宪祥,看它是不是原生指針,注意家乘,這就是traits
技法的妙處所在
如果我們只使用上面的做法蝗羊,也就是內(nèi)嵌value_type
,那么對于沒有value_type
的指針仁锯,我們只能對其進行偏特化耀找,這種偏特化是針對可調(diào)用函數(shù)get
的偏特化,假如get
有100行代碼业崖,那么就會造成極大的視覺污染
#include <iostream>
template <class T>
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template <class I>
typename I::value_type //取出迭代器類中的類型
get (I ite) {
std::cout << "class version" << std::endl;
return *ite;
}
template <class I>
I get(I* ite) {
std::cout << "pointer version" << std::endl;
return *ite;
}
template <class I>
I get(const I* ite) {
std::cout << "const pointer version" << std::endl;
return *ite;
}
int main() {
int i = 3;
const int k = 3;
MyIter<int> v(&i);
std::cout << get(v) << std::endl;
std::cout << get(&i) << std::endl;
std::cout << get(&k) << std::endl;
return 0;
}
就如同上面這個形式野芒,設(shè)想往get中填充100行代碼,簡直不忍直視双炕,你再看看下面這個狞悲,簡直優(yōu)雅!
利用一個中間層iterator_traits
固定了get
的形式妇斤,使得重復(fù)的代碼大量減少摇锋,唯一要做的就是稍稍特化一下iterator_tartis
使其支持pointer
和const pointer
:)
#include <iostream>
template <class T>
struct iterator_traits {
typedef typename T::value_type value_type;
};
template <class T>
struct iterator_traits<T*> {
typedef T value_type;
};
template <class T>
struct iterator_traits<const T*> {
typedef T value_type;
};
template <class T>
struct MyIter {
typedef T value_type;
T * ptr;
MyIter(T * p = 0) : ptr (p) {};
T& operator* () const { return *ptr;}
};
template <class I>
typename iterator_traits<I>::value_type
get (I ite) {
std::cout << "normal version" << std::endl;
return *ite;
}
int main() {
int i = 3;
const int k = 3;
MyIter<int> v(&i);
std::cout << get(v) << std::endl;
std::cout << get(&i) << std::endl;
std::cout << get(&k) << std::endl;
return 0;
}
通過定義內(nèi)嵌類型,我們獲得了知曉iterator
所指元素類型的方法站超,通過traits
技法荸恕,我們將函數(shù)模板對于原生指針和自定義iterator
的定義都統(tǒng)一起來
這就是traits
技法的妙處所在
difference type
difference type
用于表示兩個迭代器之間的距離的一個類型,也可以用來表示一個容器的最大的容量,因為對于連續(xù)空間的容器死相,頭尾之間的距離就是最大容量
例如count()
就必須返回的類型就是迭代器的difference type
對于STL容器類型戚炫,以及原生指針,traits有如下兩個不同版本
template<class I>
struct iterator_traits {
...
typedef typename I::difference_type difference_type;
}
//原生指針
template<class I>
struct iterator_traits<T*> {
...
typedef ptrdiff_t difference_type;
}
template<class I>
struct iterator_traits<const T*> {
...
typedef ptrdiff_t difference_type;
}
reference type
標(biāo)示了引用類型
pointer
標(biāo)示了指針類型
測試
以上說明了迭代器內(nèi)部的幾種重要類型
下面對其進行一個測試媳纬,以此產(chǎn)生一個更直觀的印象
#include <iostream>
#include <vector>
#define Test(x,z,y) std::cout<<std::is_same<std::iterator_traits<x>::z,y>::value<<std::endl
int main() {
#define IVec std::vector<int>::iterator
Test(IVec,value_type,int); //true
Test(IVec,difference_type,ptrdiff_t); //true
Test(IVec,reference,int&); //true
Test(IVec,pointer,int*); //true
return 0;
}
從上面可以看出双肤,一個vector<int>::iterator
-
value_type
=int
-
difference_type
=ptrdiff_t
-
reference
=int&
-
pointer
=int*
總結(jié)
要牢記iterator
是為了訪問容器內(nèi)的元素而存在的施掏,而它內(nèi)置的類型就是范型算法與容器進行溝通的重要工具
而我們使用traits
技法主要是為了解決原生指針和自定義iterator
之間的不同所造成的代碼冗余
type traits
type traits
的出現(xiàn)和STL對于性能的要求有著千絲萬縷的聯(lián)系
試想,對于vector
這種大塊分配內(nèi)存茅糜,然后大塊析構(gòu)的容器七芭,如果容器里面是POD的話,那么只要等它的生命周期結(jié)束就行了蔑赘,如果是非POD的話狸驳,那么就要判斷是否擁有no-traits
的析構(gòu)函數(shù)
如果是這樣的話,又回到了之前value_type
的窘境缩赛,因此耙箍,我們只需要使用type_traits
,對POD進行偏特化酥馍,通過兩個神奇的類型進行判斷
struct _true_type{};//無意義的析構(gòu)函數(shù)
struct _false_type{};//有意義的析構(gòu)函數(shù)
這樣子就可以讓負責(zé)析構(gòu)的模塊進行判斷了
具體的type_traits
如下所示
template<typename T>
struct type_traits
{
typedef _false_type has_trivial_default_constructor;//默認構(gòu)造函數(shù)是否有意義辩昆?
typedef _false_type has_trivial_copy_constructor;//拷貝構(gòu)造函數(shù)是否有意義?
typedef _false_type has_trivial_assgignment_constructor;//拷貝賦值操作是否有意義?
typedef _false_type has_trivial_destructor;//析構(gòu)函數(shù)是否有意義?
/*POD意指Plain Old Data,也就是標(biāo)量型別或傳統(tǒng)的C struct(傳統(tǒng)的C struct只能
包含數(shù)據(jù)成員,不能包含函數(shù)成員旨袒。也就是所謂的聚合類汁针。POD型別必然包含無意義
的ctor/dtor/copy/assignment函數(shù)。
*/
typedef _false_type is_POD_type;//是否為Plain Old Data?
};
總結(jié)
通過對type_traits
進行特化砚尽,標(biāo)注自己類中的構(gòu)造施无,拷貝等行為是否是有意義的,可以大大提高適配算法的效率必孤,這也是type traits
存在的意義