1. 程式人生 > 其它 >SGI STL型別萃取: __type_traits

SGI STL型別萃取: __type_traits

目錄

__type_traits基本概念

iterator_traits是STL針對迭代器加以規範,用來萃取迭代器特性的機制。SGI STL把這種技法擴大到迭代器以外的地方,於是有了__type_traits。(雙下劃線表示是內部所用,不在STL標準規範內)

iterator_traits 提供機制,用於萃取迭代器的5個特性:iterator_category(迭代器型別)、value_type(所指物件型別)、difference_type(迭代器做算術運算型別)、pointer(指標型別)、reference(引用型別)。

SGI中,__type_traits位於<type_traits.h>,它提供了一種機制,用於萃取不同型別屬性:在編譯期就能完成函式派生決定(function dispatch)。這對於編寫template很有幫助,比如,當我們準備對一個“元素型別未知”的陣列執行copy操作時,如果能事先知道元素型別是否有一個trivial copy constructor(平凡的拷貝建構函式),就能幫助我們決定是否使用更高效的memcpy()或memmove()。

__type_traits內嵌型別

程式中,可以這樣應用__type_traits,T代表任意型別:

__type_traits<T>::has_trivial_default_constructor
__type_traits<T>::has_trivial_copy_constructor
__type_traits<T>::has_trivial_assignment_operator
__type_traits<T>::has_trivial_destructor
__type_traits<T>::is_POD_type // POD: Plain Old Data

上面的式子,返回的是“真”或“假”,便於我們決定採用上面策略,但結果不是bool值,而是有著真/假屬性的“物件”,因為我們希望編譯器可以在編譯期,就利用響應結果來進行引數推導,而編譯器只有面對class object形式的引數,才會做引數型別推導。因此,上面式子應該傳回這樣的東西:

// 用作真、假的標記類
struct __true_type {};
struct __false_type {};

這兩個空白的class沒有任何成員,僅作為標記類,不會帶來任何負擔。

為了達成上面5個式子,在__type_traits內定義一些typedefs,其(返回)值不是__true_type,就是__false_type。下面是SGI的做法:

template<class type>
struct __type_traits
{
  typedef __true_type this_dummy_member_must_be_first;
  /* 不要移除這個成員,它通知“有能力自動將__type_traits特化的編譯器說,
  我們現在看到的這個__type_traits template是特殊的。這是為了確保萬一編譯器也使用一個名為__type_traits而其實與此處定義無任何關聯的template時,所有事情仍將順利運作”*/

  /* 以下條件應該遵守,因為編譯器有可能自動為各型別專屬的__type_traits特化版本:
    - 你可以重新安排以下的成員次序
    - 你可以移除以下任何成員
    - 絕對不可以將以下成員重新命名而沒有改變編譯器中對應名稱
    - 新加入的成員會被視為一般成員,除非你在編譯期中加上適當支援
   */
  typedef __false_type has_trivial_default_constructor;
  typedef __false_type has_trivail_copy_constructor;
  typedef __false_type has_trivail_assignment_constructor;
  typedef __false_type has_trivail_destructor;
  typedef __false_type is_POD_type; // POD: Plain Old Data
};

為什麼SGI把所有__type_traits的內嵌型別都定義為__false_type?
因為SGI定義出最保守的值,然後,再針對每個標量型別(scalar types)設計適當的__type_traits特化版本。

__type_traits可以接受任何型別的引數,5個typedefs將經由以下管道獲得實值:

  • 一般具現(general instantiation),內含對所有型別都必定有效的保守值。比如,上面各個has_trivial_xxx型別都被定義為__false_type,就是對所有型別都有效的保守值。
  • 經過宣告的特化版本,例如<type_traits.h>中對所有C++標量型別(scalar types)(指bool/char/int/float/double等基本型別)都提供了對應特化宣告。
  • 某些編譯器自動為所有型別提供適當的特化版本。

__type_traits針對C++標量型別的特化版

/* 以下針對C++基本型別char, signed char, unsignedchar, short, unsigned short,
int, unsigned int, long, unsigned long, float, double, long double 提供特化版本.
注意, 每個成員的值都是__true_type, 表示這些型別都可採用最快速方式(如memcpy)來進行拷貝(copy)或賦值(assign) */

#   define __STL_TEMPLATE_NULL template<>

__STL_TEMPLATE_NULL struct __type_traits<bool> {
   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;
};

#endif /* __STL_NO_BOOL */

__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;
};

__STL_TEMPLATE_NULL 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;
};

__STL_TEMPLATE_NULL struct __type_traits<short> {
   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<unsigned short> {
   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<int> {
   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<unsigned int> {
   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<long> {
   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<unsigned long> {
   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;
};

...

__type_traits在STL中的應用

__type_traits在SGI STL中應用很廣泛,舉幾個例子,前面這篇文章也提到過SGI STL記憶體基本處理工具:uninitialized_copy/uninitialized_fill/uninitialized_fill_n
,也有POD型別介紹。本文只挑選跟__type_traits有關的重點講。

uniitialized_fill_n()全域性函式

// __type_traits應用: uninitialized_fill_n()
template<class ForwardIterator, class Size, class T>
inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const  T& x) {
       return __uninitialized_fill_n(first, n, x, value_type(first));
}

uninitialized_fill_n()函式以x為初始化元素,自迭代器first開始構造n個元素。為求取最大效率,首先用value_type()萃取出迭代器first的value type,再用__type_traits 判斷該型別是否為POD型別:

// __type_traits萃取T的is_POD_type特性,判斷是否為POD型別
// 利用__type_traits判斷該型別是否為POD型別
template<class ForwardIterator, class Size, class T, class T1>
inline ForwardIterator __uninitialized_fiil_n(ForwardIterator first, Size n, const  T& x, T1*) {
       typedef typename __type_traits<T1>::is_POD_type is_POD;
       return __uninitialized_fiil_n_aux(first, n, x, is_POD);
}

下面就“是否為POD型別”採取最適當的措施:

// 如果不是POD型別, 就派送(dispatch)到這裡
template<class ForwardIterator, class Size, class T>
ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T&  x, __false_type) {
  ...
}

// 如果是POD型別, 就會派送(dispatch)到這裡.
template<class ForwardIterator, class Size, class T>
inline ForwardIterator ___uninitialized_fill_n_aux_(ForwardIterator first, Size n,  const T& x, __true_type) {
  ...
}

// 以下定義於<stl_algobase.h>中的fill_n()
template<class OutputIterator, class Size, class T>
OutputIterator fill_n(OutputIterator first, Size n, const T& value) {
  ...
}

負責物件析構的destroy()全域性函式

// destroy()第一個版本, 接受一個指標
template<class T>
inline void destroy(T* pointer) {
  ...
}

// destroy()第二個版本, 接受兩個迭代器. 次函式設法找出元素的數值型別,
// 進而利用__type_traits<>求取最適當措施
template<class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
  __destroy(first, last, value_type(first));
}

// __type_traits 萃取T的has_trivial_destructor特性, 判斷是否為平凡解構函式
// 判斷元素的數值型別(value type)是否有trivial 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());
}

// 如果元素的數值型別(value type)有non-trivial destructor, 則派送(dispatch)到這裡
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last,  __false_type) {
  ...
}

// 如果元素的數值型別(value type)有trivial destructor, 則派送(dispatch)到這裡
template<class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last,  __true_type) {
}

copy()全域性函式

copy()全域性函式用於拷貝。

// 利用__type_traits萃取T的has_trivial_copy_constructor特性,判斷是否為平凡copy建構函式
// 拷貝一個數組, 其元素為任意型別, 視情況採用最有效率的拷貝手段
template<class T> inline void copy(T* source, T* destination, int n) {
       copy(source, destination, n, typename  __type_traits<T>::has_trivial_copy_constructor());
}

// 拷貝一個數組, 其元素型別擁有non-trivial copy constructors, 則dispatch到這裡
template<class T> void copy(T* source, T* destination, int n, __false_type) {
       // ...
}

// 拷貝一個數組, 其元素型別擁有trivial copy constructors, 則dispatch到這裡
// 可藉助memcpy()完成工作
template<class T> void copy(T* source, T* destination, int n, __true_type) {
       // ...
}

自定義__type_traits特化版

通常,不應該直接使用__type_traits,STL內部使用。正如SGI <type_traits.h>內部有這樣的宣告:

/* NOTE: This is an internal header file, included by other STL headers.
 * You should not attempt to use it directly.
 */

因為,編譯器通常會自動為class生成這些特性,這些特性取決於自定義class是否包含trivial default constructor,trivial copy constructor,trivial assignment operator,或者trivial destructor。但有些古老的編譯器,可能並不能為class生成這些特性,導致即使是POD型別,但萃取出來的特性依然是__false_type。此時,需要自行設計一個__type_traits特化版本,明確告訴編譯器該class具有哪些特性:

// 假設自定義class名為Shape
// 針對Shape設計的__type_traits特化版本
template<> struct __type_traits<Shape> {
    typedef __true_type  has_trivial_default_constructor; // 告訴編譯器Shape擁有trivial default constructor
    typedef __false_type has_trivial_copy_constructor;
    typedef __false_type has_trivial_assignment_constructor;
    typedef __false_type has_trivial_destructor;
    typedef __false_type is_POD_type;
};

如何判斷一個class什麼時候有自己的non-trivial default constructor,non-trivial copy constructor,non-trivial assignment operator,non-trivial destructor?
一個簡單判斷原則:如果class內含指標成員,並且進行了動態記憶體配置,那麼該class就需要實現出自己的non-trivial-xxx。