STL原始碼分析 traits

2022-07-12 06:00:14 字數 4370 閱讀 7831

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...