【C++11學習筆記】型別判斷的type_traits學習
一、簡單的type_traits
我理解的type_traits是利用C++模板特性和static、enum特性定義編譯器常量,例如
//std::integral_constant原始碼
typelate<class T, T v>
struct integral_constant
{
static const T value = v;
typedef T value_type;
typedef integral_constant<T, v> type;
operator value_type() {return value;}
};
這裡利用的是static常量為編譯器常量的特,定義了value。使用方法:從std::integral_constant派生,無需自己定義static const常量或enum型別,例如
template<typename T>
struct GetSize : std::integral_constant<int, 1>
{
};
std有兩個定義好的std::integral_constant例項,分別定義了編譯期的true和false型別,用途很廣:
typedef integral_constant<bool, true> true _type;
typedef integral_constant<bool, false> false_type;
二、常見型別判斷type_traits原始碼學習
1.is_void
宣告:
template<class T>
struct is_void;
作用:
T是否為void型別
原始碼:
template<class T, class U>
struct is_same : std::false_type
{};
template<class T>
struct is_same : std::true_type
{};
template<class T>
struct is_void : std::is_same<void, typename std::remove_cv<T>::type>
{};
說明:首先利用模板的匹配實現用以判斷兩種型別是否一致的is_name,再將T去除c(const)、v(volatile)限定符後與void型別判斷是否一致。下面有些簡單的程式碼就不解釋了。
2.is_floating_point
宣告
template< class T >
struct is_floating_point;
作用
T是否為浮點型別
原始碼
template< class T >
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.is_array
宣告
template<class T>
struct is_array;
作用
T是否為陣列型別
原始碼
template<class T>
struct is_array : std::false_type {};
template<class T>
struct is_array<T[]> : std::true_type {};
template<class T, std::size_t N>
struct is_array<T[N]> : std::true_type {};
4.is_pointer
宣告
template< class T >
struct is_pointer;
作用
T是否為指標型別(包括函式指標,但不包括成員(函式)指標)
原始碼
template< class T > struct is_pointer_helper : std::false_type {};
template< class T > struct is_pointer_helper<T*> : std::true_type {};
template< class T > struct is_pointer : is_pointer_helper<typename std::remove_cv<T>::type> {};
5.is_member_pointer
宣告
template< class T >
struct is_member_pointer
作用
T是否為成員函式指標、指向成員變數指標型別
原始碼
template< class T >
struct is_member_pointer_helper : std::false_type {};
template< class T, class U >
struct is_member_pointer_helper<T U::*> : std::true_type {};
template< class T >
struct is_member_pointer : is_member_pointer_helper<typename std::remove_cv<T>::type>
{};
為什麼is_member_pointer_helper< T U::*>這個就是成員函式指標、指向成員變數指標型別呢?
可以見我另外一篇文章:C++如何宣告類成員函式指標或類成員變數指標(A::*)。
這個引數T U::*怎麼理解,其實就理解成T *——T型別指標,但是是類U中的,即類U的成員函式指標或成員變數指標,看下面的測試程式碼:
#include <iostream>
#include <type_traits>
int main() {
class cls {};
std::cout << (std::is_member_pointer<int(cls::*)>::value
? "T is member pointer"
: "T is not a member pointer") << '\n';
std::cout << (std::is_member_pointer<int cls::*>::value
? "T is member pointer"
: "T is not a member pointer") << '\n';
}
輸出是
T is member pointer
T is member pointer
注意,並不是判斷類T中是否真的有返回值為int的函式,或者是否有int型變數,而是隻是判斷T這個寫法是否是成員函式指標、指向成員變數指標型別。
6.is_class
宣告:
template <class T>
struct is_class;
作用
T是否為類型別,且不是union型別
原始碼
namespace detail {
template <class T> char test(int T::*);
struct two { char c[2]; };
template <class T> two test(...);
}
template <class T>
struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1 && !std::is_union<T>::value>
{};
解釋一下,定義了兩個模板函式,一個形參是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
宣告
template <typename Base, typename Derived>
class is_base_of;
作用
Base是否是Derived的基類
原始碼
template <typename Base, typename Derived,
bool = (is_class<Base>::value && is_class<Derived>::value)>
class is_base_of
{
template <typename T>
static char helper(Derived, T);
static int helper(Base, int);
struct Conv
{
operator Derived();
operator Base() const;
};
public:
static const bool value = sizeof(helper(Conv(), 0)) == 1;
};
template <typename Base, typename Derived>
class is_base_of<Base, Derived, false>
{
public:
static const bool value = is_same<Base, Derived>::value;
};
class B
{
};
class D : public B
{
};
int main()
{
cout << boolalpha << is_base_of<B, D>::value << endl;
}
程式碼中最“厲害”的地方就是對helper函式的匹配了。
如果Base不是Derived的基類,那麼Conv()做隱式轉換時,兩個候選型別Base和Derived都是平等的,兩個helper函式都可以匹配,但在這裡按照規則,會去優先匹配非模板的函式。於是得到了我們想要的結果。
如果Base是Derived的基類,這種情況比較複雜。
這種情況下,除非Conv物件是一個const的,否則它的隱式轉換是隻會去呼叫operator ()Derived的,因為operator ()Base const後面所帶的const。於是這樣的情況下,Conv()總是隱式轉換成一個Derived物件(當然,上面的只是學習程式碼,如果真正要實用的話,還要做很多工作,比如說在這裡就要首先確保Derived型別本身不是const的),這時候對於兩個helper的第一個引數,一個是精確匹配,一個是要轉換為基類,一開始我異想天開地以為這種情況下就會去先匹配第一個了,因為這也是所需要的正確結果,結果自然是沒有錯,不過我的想法卻是太天真了,因為我完全抹殺了第二個引數的貢獻,倘若沒有它,那第一個helper函式都不會被具現化,更別說讓它去被匹配了。