一、簡單的type_traits
我理解的type_traits是利用C++模板特性和static、enum特性定義編譯器常量,例如
1 // TEMPLATE CLASS integral_constant 2 template<class _Ty, 3 _Ty _Val> 4 struct integral_constant 5 { // convenient template for integral constant types 6 static constexpr _Ty value = _Val; 7 8 typedef _Ty value_type; 9 typedef integral_constant<_Ty, _Val> type; 10 11 constexpr operator value_type() const _NOEXCEPT 12 { // return stored value 重載了強制轉換 (例如 (bool)integral_constant<bool,true> 將會返回value值true) 13 return (value); 14 } 15 16 constexpr value_type operator()() const _NOEXCEPT 17 { // return stored value 重載了() (例如, integral_constant<bool, true)() 將會返回value值 true) 18 return (value); 19 } 20 };
這里利用的是static常量為編譯器常量的特點,定義了value。使用方法:從std::integral_constant派生,無需自己定義static const常量或enum類型,
std有兩個定義好的std::integral_constant實例,分別定義了編譯期的true和false類型,用途很廣:
1 typedef integral_constant<bool, true> true_type; 2 typedef integral_constant<bool, false> false_type;
二、常見類型判斷type_traits源碼學習
1.is_void
聲明:
template<class T> struct is_void;
作用:
T是否為void類型
源碼:
1 template<class T, class U> 2 struct is_same : std::false_type 3 {}; 4 5 template<class T> 6 struct is_same : std::true_type 7 {}; 8 9 template<class T> 10 struct is_void : std::is_same<void, typename std::remove_cv<T>::type> 11 {};
說明:首先利用模板的匹配實現用以判斷兩種類型是否一致的is_name,再將T去除c(const)、v(volatile)限定符后與void類型判斷是否一致。下面有些簡單的代碼就不解釋了。
2.is_floating_point
聲明
1 template< class T > 2 struct is_floating_point;
作用
T是否為浮點類型
源碼
1 template< class T > 2 struct is_floating_point : std::integral_constant<bool,std::is_same<float, typename std::remove_cv<T>::type>::value || std::is_same<double, typename std::remove_cv<T>::type>::value || std::is_same<long double, typename std::remove_cv<T>::type>::value> 3 {};
3.is_array
聲明
1 template<class T> 2 struct is_array;
作用
T是否為數組類型
源碼
1 template<class T> 2 struct is_array : std::false_type {}; 3 4 template<class T> 5 struct is_array<T[]> : std::true_type {}; 6 7 template<class T, std::size_t N> 8 struct is_array<T[N]> : std::true_type {};
4.is_pointer
聲明
1 template< class T > 2 struct is_pointer;
作用
T是否為指針類型(包括函數指針,但不包括成員(函數)指針)
源碼
1 template< class T > struct is_pointer_helper : std::false_type {}; 2 template< class T > struct is_pointer_helper<T*> : std::true_type {}; 3 template< class T > struct is_pointer : is_pointer_helper<typename std::remove_cv<T>::type> {};
5.is_member_pointer
聲明
1 template< class T > 2 struct is_member_pointer
作用
T是否為成員函數指針、指向成員變量指針類型
源碼
1 template< class T > 2 struct is_member_pointer_helper : std::false_type {}; 3 4 template< class T, class U > 5 struct is_member_pointer_helper<T U::*> : std::true_type {}; 6 7 template< class T > 8 struct is_member_pointer : is_member_pointer_helper<typename std::remove_cv<T>::type> 9 {};
為什么is_member_pointer_helper< T U::*>這個就是成員函數指針、指向成員變量指針類型呢?
這個參數T U::*怎么理解,其實就理解成T *——T類型指針,但是是類U中的,即類U的成員函數指針或成員變量指針,看下面的測試代碼:
1 #include <iostream> 2 #include <type_traits> 3 4 int main() { 5 class cls {}; 6 std::cout << (std::is_member_pointer<int(cls::*)>::value 7 ? "T is member pointer" 8 : "T is not a member pointer") << '\n'; 9 std::cout << (std::is_member_pointer<int cls::*>::value 10 ? "T is member pointer" 11 : "T is not a member pointer") << '\n'; 12 }
輸出是
T is member pointer T is member pointer
注意,並不是判斷類T中是否真的有返回值為int的函數,或者是否有int型變量,而是只是判斷T這個寫法是否是成員函數指針、指向成員變量指針類型。
6.is_class
聲明:
1 template <class T> 2 struct is_class;
作用
T是否為類類型,且不是union類型
源碼
1 namespace detail { 2 template <class T> char test(int T::*); 3 struct two { char c[2]; }; 4 template <class T> two test(...); 5 } 6 7 template <class T> 8 struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1 && !std::is_union<T>::value> 9 {};
解釋一下,定義了兩個模板函數,一個形參是int T::*(指向int型類成員變量的指針),返回值是char(大小是1);另一個形參是所有類型,返回值是struct two(大小是2)。
is_class繼承了std::integral_constant< T, T v >(內部定義了一個static const T類型變量value,取值為v),value的類型為bool,當detail::test(0)的大小為1時(只要T是class類型,就符合第一個模板函數test,則其返回值大小就為1,否則返回值大小為2),並且不為union類型時(加上這個是因為,union類型類似struct類型,也支持T::*),則為class(或struct)類型。
7.is_base_of
聲明
1 template <typename Base, typename Derived> 2 class is_base_of;
作用
Base是否是Derived的基類
源碼
1 template <typename Base, typename Derived, 2 bool = (is_class<Base>::value && is_class<Derived>::value)> 3 class is_base_of 4 { 5 template <typename T> 6 static char helper(Derived, T); 7 static int helper(Base, int); 8 struct Conv 9 { 10 operator Derived(); 11 operator Base() const; 12 }; 13 public: 14 static const bool value = sizeof(helper(Conv(), 0)) == 1; 15 }; 16 17 18 template <typename Base, typename Derived> 19 class is_base_of<Base, Derived, false> 20 { 21 public: 22 static const bool value = is_same<Base, Derived>::value; 23 }; 24 25 26 class B 27 { 28 }; 29 30 class D : public B 31 { 32 }; 33 34 int main() 35 { 36 cout << boolalpha << is_base_of<B, D>::value << endl; 37 }
代碼中最“厲害”的地方就是對helper函數的匹配了。
-
如果Base不是Derived的基類,那么Conv()做隱式轉換時,兩個候選類型Base和Derived都是平等的,兩個helper函數都可以匹配,但在這里按照規則,會去優先匹配非模板的函數。於是得到了我們想要的結果。
-
如果Base是Derived的基類,這種情況比較復雜。
這種情況下,除非Conv對象是一個const的,否則它的隱式轉換是只會去調用operator ()Derived的,因為operator ()Base const后面所帶的const。於是這樣的情況下,Conv()總是隱式轉換成一個Derived對象(當然,上面的只是學習代碼,如果真正要實用的話,還要做很多工作,比如說在這里就要首先確保Derived類型本身不是const的),這時候對於兩個helper的第一個參數,一個是精確匹配,一個是要轉換為基類,一開始我異想天開地以為這種情況下就會去先匹配第一個了,因為這也是所需要的正確結果,結果自然是沒有錯,不過我的想法卻是太天真了,因為我完全抹殺了第二個參數的貢獻,倘若沒有它,那第一個helper函數都不會被具現化,更別說讓它去被匹配了。
【參考:
1.《深入應用C++11代碼優化與工程級應用》
2.http://en.cppreference.com/w/
3.TR1中is_base_of的實現】