【C++11學習筆記】類型判斷的type_traits學習


一、簡單的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函數的匹配了。

  1. 如果Base不是Derived的基類,那么Conv()做隱式轉換時,兩個候選類型Base和Derived都是平等的,兩個helper函數都可以匹配,但在這里按照規則,會去優先匹配非模板的函數。於是得到了我們想要的結果。

  2. 如果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的實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM