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
在實現上,
我們注意到,這裡使用__type_traits
用於識別_tp
型別是否trivial
template void
__destroy_aux(_forwarditerator __first, _forwarditerator __last, __false_type)
template inline void __destroy_aux(_forwarditerator, _forwarditerator, __true_type) {}
template inline void
__destroy(_forwarditerator __first, _forwarditerator __last, _tp*)
template inline void _destroy(_forwarditerator __first, _forwarditerator __last)
也許你的問題來了,什麼樣的型別才可稱之為trivial的?
如果乙個型別滿足以下條件中的至少乙個,則稱其為非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
分別表示邏輯真/假型別。
從**可以看到,預設情況下,_tp
中預設建構函式/複製建構函式/賦值操作符/析構函式都不是trivial
的,_tp
也不是pod型別。
template struct __type_traits ;
struct __true_type ;
struct __false_type ;
其次,對於所有標量型別,定義特化模板類__type_traits
,因為標量型別,沒有定義預設構造/複製構造/複製操作符/析構函式,標量型別也屬於pod型別。標量型別包含:
bool
char
signed char
unsigned char
wchar_t
short
unsigned short
intunsigned 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
是整數型別。在這種情況下,如果不分割槽整數型別,那麼編譯器便無法區分vectora(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)
// 建構函式2
// check whether it's an integral type. if so, it's not an iterator.
template vector(_inputiterator __first, _inputiterator __last,
const allocator_type& __a = allocator_type()) : _base(__a)
首先,定義通用模板函式_is_integer
。預設情況下所有的型別都不是整型。
template struct _is_integer ;
其次,定義特化模板函式_is_integer
,對於以下型別,_integral
為__true_type
。
bool
char
signed char
unsigned char
wchar_t
short
unsigned short
intunsigned int
long
unsigned long
long long
unsigned long long
ps: stl中遠遠不止上述兩種traits,例如還有__char_traits
(見stl原始碼分析--string)、iterator_traits
(見stl原始碼分析--iterator)、_alloc_traits
(見stl原始碼分析--記憶體分配器),在此留給讀者自行分析
推薦閱讀
《STL原始碼剖析》traits技法分析
在完成乙個迭代器的時候,我們可能會暴露太多的細節在外面,為了將這些細節給隱藏,我們需要封裝,這也是為什麼每一種stl容器都提供了一種專屬的迭代器。為了解決以 迭代器所指物件的型別 為型別 解決辦法是 利用template的引數推導 argument deducation template void ...
STL原始碼 traits的使用
關於iterator traits和type traits的使用,我們什麼時候會使用這兩個類?我們怎麼使用?問題 將 first1,last1 區間內的元素複製一遍。我們需要知道first1迭代器的型別。template iter copy iter first1,iter last1 else n...
STL原始碼分析set
include include using namespace std int main set iset ia,ia 5 cout size iset.size endl cout 3 count iset.count 3 endl iset.insert 3 cout size iset.siz...