更多精彩內容,請關注微信公衆號:後端技術小屋編程
traits
(譯做萃取)是C++中一種特殊的編程技法,它是模板元編程最直接的用例之一。經過traits
,能夠抽取模板入參類型的各類屬性。接下來咱們經過STL中最多見的幾種traits
舉例說明。後端
__type_traits
用於判斷類型是否爲trival(譯做平凡)。數組
若是一個類型是trivial
的,則能夠靜態初始化,能夠用memcpy直接複製數據而不是必須用copy構造函數。其生存期始於它的對象存儲被定義,無需等到構造函數完成。在執行ctor, copy, move, assign, ctor時,能夠採用最有效率的方法:即不執行編譯器自動生成的ctor, copy, assign, ctor, 取而代之的是malloc, free, memcpy這類操做。微信
舉個栗子:_Destroy
用於銷燬容器迭代器區間[__first, __last)
內全部對象。
而其調用鏈以下:函數
_Destroy -> __destroy -> __destroy_aux
在實現上,源碼分析
_Destory
首先使用__VALUE_TYPE
獲取指向容器中對象的指針類型,並將其實例做爲入參傳入到__destory
中。__destory
中,使用__type_traits<_Tp>::has_trivial_destructor
判斷容器中對象類型是否爲trivial
,並調用__destroy_aux
。__destroy_aux
函數根據_Tp
是否爲trivial
實現了兩個不一樣的版本。在trivial
版本中,什麼都不作,由於trivial
類型沒有顯式定義析構函數。在非trivial
函數中,則必須調用_Tp
的析構函數。咱們注意到,這裏使用__type_traits
用於識別_Tp
類型是否trivial
佈局
template <class _ForwardIterator> void __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type) { for ( ; __first != __last; ++__first) destroy(&*__first); } template <class _ForwardIterator> inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {} template <class _ForwardIterator, class _Tp> inline void __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*) { typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor; __destroy_aux(__first, __last, _Trivial_destructor()); } template <class _ForwardIterator> inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) { __destroy(__first, __last, __VALUE_TYPE(__first)); }
也許你的問題來了,什麼樣的類型纔可稱之爲trivial的?this
若是一個類型知足如下條件中的至少一個,則稱其爲非trival;不然稱其爲trival 1. 顯式定義了構造函數(ctor), 複製構造函數(copy), 移動構造函數(move),賦值運算符(assign), 或析構函數(ctor)之中任何一個。 2. 類中有非POD類型成員 3. 有虛函數 4. 有虛基類
到這裏也許你的問題會更多了,到底什麼是POD
類型?指針
POD = Plain Old Data 根據維基百科的定義,POD類型包括標量類型和POD類類型。POD在源代碼兼容於ANSI C時很是重要。POD對象與C語言的對應對象具備共同的一些特性,包括初始化,複製,內存佈局,尋址等。 標量類型包括: 1. 算數類型(整數/浮點/字符/布爾) 2. 枚舉類型 3. 指針類型(空指針/對象指針/函數指針) 4. 指針到成員類型(例如T C::* 指向類C的類型爲T的數據成員的指針) POD類類型是指聚合類(經過struct/union聚合)或數組,也不具備下述成員: 1. 指針到成員類型的非靜態數據成員 2. 非POD類型的非靜態數據成員 3. 引用類型的非靜態數據成員 4. 顯式定義的拷貝和賦值算子 5. 顯式定義的析構函數 6. 若是是聚合類且含有顯式定義的構造函數,私有/保護的非靜態成員函數,基類,虛函數 綜上,不符合以上6條的聚合類/數組纔可稱之爲POD類類型。
在實現上,首先定義通用模板類__type_traits
。這裏全部的計算都是基於類型的,所以使用__true_type
和__false_type
分別表示邏輯真/假類型。code
從代碼能夠看到,缺省狀況下,_Tp
中默認構造函數/複製構造函數/賦值操做符/析構函數都不是trivial
的,_Tp
也不是POD類型。
template <class _Tp> struct __type_traits { typedef __true_type this_dummy_member_must_be_first; typedef __false_type has_trivial_default_constructor; typedef __false_type has_trivial_copy_constructor; typedef __false_type has_trivial_assignment_operator; typedef __false_type has_trivial_destructor; typedef __false_type is_POD_type; }; struct __true_type { }; struct __false_type { };
其次,對於全部標量類型,定義特化模板類__type_traits
,由於標量類型,沒有定義默認構造/複製構造/複製操做符/析構函數,標量類型也屬於POD類型。標量類型包含:
bool char signed char unsigned char wchar_t short unsigned short int unsigned int long unsigned long unsigned long long float double long double Tp* char* signed char* unsigned char*, const char* const signed char* const unsigned char*
_Is_integer用於判斷類型是否爲整數類型
舉個例子:若是vector中元素類型_Tp
是整數類型。在這種狀況下,若是不分區整數類型,那麼編譯器便沒法區分vector<int> a(10, 1);
該使用如下代碼中第一種構造函數仍是第二種,由於_InputIterator
只是一個模板參數,它能夠是真正的迭代器類型,也能夠是其餘任何一種類型。
爲了避免使編譯器犯難,咱們須要在編譯期間決定_InputIterator
是否爲整數類型。這裏用到了_Is_integer
,它斷定_InputIterator
類型,並返回__true_type
或__false_type
。對應的,_M_initialize_aux
針對_InputIterator
是否爲整形也實現了兩個版本。最終使得vector
構造在兩種不一樣狀況下,保持了各自的語義:
// 構造函數1 vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } // 構造函數2 // Check whether it's an integral type. If so, it's not an iterator. template <class _InputIterator> vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { typedef typename _Is_integer<_InputIterator>::_Integral _Integral; _M_initialize_aux(__first, __last, _Integral()); }
首先,定義通用模板函數_Is_integer
。缺省狀況下全部的類型都不是整型。
template <class _Tp> struct _Is_integer { typedef __false_type _Integral; };
其次,定義特化模板函數_Is_integer
,對於如下類型,_Integral
爲__true_type
。
bool char signed char unsigned char wchar_t short unsigned short int unsigned int long unsigned long long long unsigned long long
PS: STL中遠遠不止上述兩種traits,例如還有__char_traits
(見STL源碼分析--string)、iterator_traits
(見STL源碼分析--iterator)、_Alloc_traits
(見STL源碼分析--內存分配器),在此留給讀者自行分析
更多精彩內容,請關注微信公衆號: 悟空者說
推薦閱讀
更多精彩內容,請掃碼關注微信公衆號:後端技術小屋。若是以爲文章對你有幫助的話,請多多分享、轉發、在看。