C++ type traits分析
我們在平時常常會聽到有人說traits/萃取等高大上的東西,有時候可能也會對此產生很大的疑問,覺得type tratis很高大上,高深莫測;其實說到底這個東西很簡單,總結爲一句話就是在運行的時候識別類型(即類型萃取)。
本文我們大致看一下type traits的基本實現技術。
1. integral_constant
瞭解萃取機之前,我們先了解一下integral_constant
, 這個在C++庫中定義爲一個常量的整數,定義如下:
template<class _Ty,
_Ty _Val>
struct integral_constant
{ // convenient template for integral constant types
static constexpr _Ty value = _Val;
using value_type = _Ty;
using type = integral_constant;
constexpr operator value_type() const noexcept
{ // return stored value
return (value);
}
_NODISCARD constexpr value_type operator()() const noexcept
{ // return stored value
return (value);
}
};
這個的主要核心是定義了一個靜態常量值:static constexpr _Ty value = _Val;
.
爲什麼需要定義這樣一個東西呢?我們不直接使用_Ty value = _Val
定義一個全局的變量不是挺好的嘛,爲啥需要搞的那麼麻煩呢?
主要原因是:爲了C++編譯的時候能夠使用模板初編譯來確定其中的值。
從integral_constant
引申出來了兩個東西:
true_type
。false_type
。
這兩個東西分別代表TRUE 和 FALSE,如下:
template<bool _Val>
using bool_constant = integral_constant<bool, _Val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
2. C++庫的type traits
2.1 Primary type categories
2.2 Composite type categories
2.3 Type properties
2.4 Type features
2.5 Type relationships
2.6 Property queries
2.7 Type transformations
3. type traits的例子
class CData1
{
public:
CData1() {}
virtual ~CData1() {}
};
class CData2
{
public:
CData2() {}
~CData2() {}
};
class CData3
{
public:
int a;
int b;
int c;
};
int main(int args, char* argv[])
{
std::cout << "CData1 has_virtual_destructor : " << std::has_virtual_destructor<CData1>::value << std::endl;
std::cout << "CData2 has_virtual_destructor : " << std::has_virtual_destructor<CData2>::value << std::endl;
std::cout << "CData3 has_virtual_destructor : " << std::has_virtual_destructor<CData3>::value << std::endl;
std::cout << "CData1 is_pod : " << std::is_pod<CData1>::value << std::endl;
std::cout << "CData2 is_pod : " << std::is_pod<CData2>::value << std::endl;
std::cout << "CData3 is_pod : " << std::is_pod<CData3>::value << std::endl;
return 0;
}
輸出結果如下:
CData1 has_virtual_destructor : 1
CData2 has_virtual_destructor : 0
CData3 has_virtual_destructor : 0
CData1 is_pod : 0
CData2 is_pod : 0
CData3 is_pod : 1
從上面我們可以看到type traits是非常厲害的,他能夠在編譯器的時候知道C++定義類型的所有屬性。
4. type tratis的實現
我們看幾個例子來大致看一下type traits的實現原理.
4.1 std::is_integral
std::is_integral
用來判斷一個類型是否是整數,這個的實現原理如下:
// STRUCT TEMPLATE _Is_integral
template<class _Ty>
struct _Is_integral
: false_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<bool>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<char>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<unsigned char>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<signed char>
: true_type
{ // determine whether _Ty is integral
};
#ifdef _NATIVE_WCHAR_T_DEFINED
template<>
struct _Is_integral<wchar_t>
: true_type
{ // determine whether _Ty is integral
};
#endif /* _NATIVE_WCHAR_T_DEFINED */
template<>
struct _Is_integral<char16_t>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<char32_t>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<unsigned short>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<short>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<unsigned int>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<int>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<unsigned long>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<long>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<unsigned long long>
: true_type
{ // determine whether _Ty is integral
};
template<>
struct _Is_integral<long long>
: true_type
{ // determine whether _Ty is integral
};
// STRUCT TEMPLATE is_integral
template<class _Ty>
struct is_integral
: _Is_integral<remove_cv_t<_Ty>>::type
{ // determine whether _Ty is integral
};
首先定義了一個template<class _Ty> struct _Is_integral : false_type
通用的模板,這個模板中有一個bool value = false
的靜態成員。
然後就是真的所有的整數類型,創建特化模塊,例如如下:
template<>
struct _Is_integral<int>
: true_type
{ // determine whether _Ty is integral
};
這個模板中有一個bool value = true
的靜態成員.
從這裏大致我們可以看出type traits是使用特化來確定特定的情況。
4.2 std::is_pod
對於簡單類型的判斷比較容易,我們實現所有類型的模板特化即可,但是對於類複雜類型的判斷,就比較麻煩了,C++標準庫的實現如下:
template<class _Ty>
struct is_pod
: bool_constant<__is_pod(_Ty)>
{ // determine whether _Ty is a POD type
};
template<class _Ty>
_INLINE_VAR constexpr bool is_pod_v = __is_pod(_Ty);
// STRUCT TEMPLATE is_empty
template<class _Ty>
struct is_empty
: bool_constant<__is_empty(_Ty)>
{ // determine whether _Ty is an empty class
};
template<class _Ty>
_INLINE_VAR constexpr bool is_empty_v = __is_empty(_Ty);
// STRUCT TEMPLATE is_polymorphic
template<class _Ty>
struct is_polymorphic
: bool_constant<__is_polymorphic(_Ty)>
{ // determine whether _Ty is a polymorphic type
};
對於__is_pod
C++標準庫並沒有公開的代碼,這裏也不知道具體如何實現,跟編譯器的底層實現細節有關,但是從我們所有的type traits來說,這個功能還是十分強大的。
5. iterator_traits
在萃取中,存在一個比較重要的萃取,如果上面的is_class
, is_pod
都沒有用過的話,那麼iterator_traits
這個萃取機肯定是用過的,例如:
template<typename _InputIterator, typename _Size, typename _ForwardIterator>
inline _ForwardIterator
uninitialized_copy_n(_InputIterator __first, _Size __n,
_ForwardIterator __result)
{ return std::__uninitialized_copy_n(__first, __n, __result,
std::__iterator_category(__first)); }
其中std::__iterator_category(__first))
這個就是類型萃取機,這個實現如下:
template<typename _Iter>
inline _GLIBCXX_CONSTEXPR
typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{ return typename iterator_traits<_Iter>::iterator_category(); }
iterator_traits
這個就是迭代器的萃取機,這個萃取機可以做如下事情:
- 萃取迭代器的類型。
- 萃取迭代器代表的值的類型。
- 萃取迭代器使用值的引用指針等類型。
這個迭代器實現如下:
template<typename _Iterator>
struct iterator_traits
{
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
/// Partial specialization for pointer types.
template<typename _Tp>
struct iterator_traits<_Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
/// Partial specialization for const pointer types.
template<typename _Tp>
struct iterator_traits<const _Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
對於我們STL的迭代器,都需要定義這些類型:
Iterator::iterator_category
迭代器類型。Iterator::value_type
: 迭代器的值類型。Iterator::difference_type
: 迭代器的距離信息。Iterator::pointer
: 迭代器指針。Iterator::reference
: 迭代器的引用。
STL的迭代器其實就是模擬指針來實現的,所以指針,應該天生適合最合適的迭代器,因此給_Tp*
和 const _Tp*
定義了特殊的萃取類型。
6. 總結
從上面分析,對於C++庫,萃取的實現一般都是定義模板來實現,對於普通的類型,匹配這個模板的定義;然後針對特殊類型實現特化模板支持。