STL原始碼分析之__type_traits型別
前言
上一篇探討的是traits
是為了將迭代器沒能完善的原生指標, traits
用特化和偏特化程式設計來完善. 這一篇準備探討__type_traits
, 為了將我們在空間配置器裡面的提過的__true_type
和false_type
進行解答. 而type_traits
型別對我們STL的效率又有什麼影響, 有什麼好處?
__type_traits介紹
前面介紹的Traits技術在STL中彌補了C++模板的不足,但是Traits技術只是用來規範迭代器,對於迭代器之外的東西沒有加以規範。因此,SGI將該技術擴充套件到迭代器之外,稱為__type_traits
。iterator_traits是萃取迭代器的特性,而__type_traits是萃取型別的特性。萃取的型別如下:
- 是否具備non-trivial default ctor?
- 是否具備non-trivial copy ctor?
- 是否具備non-trivial assignment operator?
- 是否具備non-trivial dtor?
- 是否為POD(plain old data)型別?
其中non-trivial意指非預設的相應函式,編譯器會為每個類構造以上四種預設的函式,如果沒有定義自己的,就會用編譯器預設函式,如果使用預設的函式,我們可以使用memcpy(),memmove(),malloc()等函式來加快速度,提高效率.
且__iterator_traits
__true_type
和__false_type
來區分.
空間配置器的例子
關於__false_type
也在空間配置器提過. 現在再來看一看.
// 接受兩個迭代器, 以__type_trais<> 判斷是否有traival destructor
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
{
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
}
// non-travial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
{
for ( ; first < last; ++first)
destroy(&*first);
}
// travial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
通過函式trivial_destructor()
確定型別來執行更高效的函式. 而且過載函式的選擇實在編譯時期就確定了. 馬上就來探討__false_type
和__true_type
引數推導.
兩個引數推導
struct __true_type {};
struct __false_type {};
我們不能將引數設為bool值, 因為需要在編譯期就決定該使用哪個函式, 所以需要利用函式模板的引數推導機制, 將__true_type
和__false_type
表現為一個空類, 就不會帶來額外的負擔, 又能表示真假, 還能在編譯時型別推導就確定執行相應的函式.
__type_traits原始碼
__STL_TEMPLATE_NULL struct __type_traits<char> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
__STL_TEMPLATE_NULL struct __type_traits<signed char> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
...
以上是將基礎的型別都設定為__true_type
型別.
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
template <class T>
struct __type_traits<T*> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
struct __type_traits<char*> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
struct __type_traits<signed char*> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
struct __type_traits<unsigned char*> {
typedef __true_type has_trivial_default_constructor;
typedef __true_type has_trivial_copy_constructor;
typedef __true_type has_trivial_assignment_operator;
typedef __true_type has_trivial_destructor;
typedef __true_type is_POD_type;
};
這裡將指標進行特化處理, 同樣是__true_type
型別.
SGI將所有的內嵌型別都定義為false_type
, 這是對所有的定義最保守的值.
template <class type>
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;
};
從上面的原始碼可以明白, 所有的基本型別都是設定的為__true_type
型別, 而所有的物件都會被特化為__false_type
型別, 這是為了保守而不得這樣做. 也因為__true_type
型別讓我們在執行普通型別時能夠以最大效率進行對其的copy, 析構等操作.
總結
SGI對traits
進行擴充套件,使得所有型別都滿足traits
程式設計規範, 這樣SGI STL演算法可以通過__type_traits
獲取型別資訊在編譯期間就能決定出使用哪一個過載函式, 解決了template
是在執行時決定過載選擇的問題. 並且通過true
和false
來確定POD和travial destructor, 讓程式能選擇更加符合其型別的處理函式, 大大提高了對基本型別的快速處理能力並保證了效率最高.