前言
我们在平时常常会听到有人说traits/萃取等高大上的东西,有时候可能也会对此产生很大的疑问,觉得type tratis很高大上,高深莫测;其实说到底这个东西很简单,总结为一句话就是在运行的时候识别类型(即类型萃取)。
integral_constant
了解萃取机之前,我们先了解一下integral_constant,这个在C++库中定义为一个常量的整数,定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template<class _Ty, _Ty _Val> struct integral_constant { static constexpr _Ty value = _Val; using value_type = _Ty; using type = integral_constant; constexpr operator value_type() const noexcept { return (value); } _NODISCARD constexpr value_type operator()() const noexcept { return (value); } };
|
这个的主要核心是定义了一个静态常量值:
1
| static constexpr _Ty value = _Val;
|
为什么需要定义这样一个东西呢?我们不直接使用_Ty value = _Val定义一个全局的变量不是挺好的嘛,为啥需要搞的那么麻烦呢?
主要原因是:为了C++编译的时候能够使用模板初编译来确定其中的值。
从integral_constant引申出来了两个东西:
true_type
false_type
这两个东西分别代表TRUE 和 FALSE,如下:
1 2 3 4
| template<bool _Val> using bool_constant = integral_constant<bool, _Val>; using true_type = bool_constant<true>; using false_type = bool_constant<false>;
|
C++库的type traits
Primary type categories
Composite type categories
Type properties
Type features
Type relationships
Property queries
type traits的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class CData1 { public: CData1() {} virtual ~CData1() {} }; class CData2 { public: CData2() {} ~CData2() {} }; class CData3 { public: int a; int b; int c; }; int main() { 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; }
|
输出结果如下:
1 2 3 4 5 6
| 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++定义类型的所有属性。
type tratis的实现
我们看几个例子来大致看一下type traits的实现原理。
std::is_integral
std::is_integral用来判断一个类型是否是整数,这个的实现原理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| template<class _Ty> struct _Is_integral : false_type { }; template<> struct _Is_integral<bool> : true_type { }; template<> struct _Is_integral<char> : true_type { }; template<> struct _Is_integral<unsigned char> : true_type { }; template<> struct _Is_integral<signed char> : true_type { }; #ifdef _NATIVE_WCHAR_T_DEFINED template<> struct _Is_integral<wchar_t> : true_type { }; endif template<> struct _Is_integral<char16_t> : true_type { }; template<> struct _Is_integral<char32_t> : true_type { }; template<> struct _Is_integral<unsigned short> : true_type { }; template<> struct _Is_integral<short> : true_type { }; template<> struct _Is_integral<unsigned int> : true_type { }; template<> struct _Is_integral<int> : true_type { }; template<> struct _Is_integral<unsigned long> : true_type { }; template<> struct _Is_integral<long> : true_type { }; template<> struct _Is_integral<unsigned long long> : true_type { }; template<> struct _Is_integral<long long> : true_type { }; template<class _Ty> struct is_integral : _Is_integral<remove_cv_t<_Ty>>::type { };
|
首先定义了一个template struct _Is_integral : false_type 通用的模板,这个模板中有一个bool value = false的静态成员。
然后就是真的所有的整数类型,创建特化模块,例如如下:
1 2 3 4 5
| template<> struct _Is_integral<int> : true_type { };
|
这个模板中有一个bool value = true的静态成员。
从这里大致我们可以看出type traits是使用特化来确定特定的情况。
std::is_pod
对于简单类型的判断比较容易,我们实现所有类型的模板特化即可,但是对于类复杂类型的判断,就比较麻烦了,C++标准库的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| template<class _Ty> struct is_pod : bool_constant<__is_pod(_Ty)> { }; template<class _Ty> _INLINE_VAR constexpr bool is_pod_v = __is_pod(_Ty); template<class _Ty> struct is_empty : bool_constant<__is_empty(_Ty)> { }; template<class _Ty> _INLINE_VAR constexpr bool is_empty_v = __is_empty(_Ty); template<class _Ty> struct is_polymorphic : bool_constant<__is_polymorphic(_Ty)> { };
|
对于__is_podC++标准库并没有公开的代码,这里也不知道具体如何实现,跟编译器的底层实现细节有关,但是从我们所有的type traits来说,这个功能还是十分强大的。
iterator_traits
在萃取中,存在一个比较重要的萃取,如果上面的is_class,is_pod都没有用过的话,那么iterator_traits这个萃取机肯定是用过的,例如:
1 2 3 4 5
| 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))这个就是类型萃取机,这个实现如下:
1 2 3 4 5
| 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 这个就是迭代器的萃取机,这个萃取机可以做如下事情:
萃取迭代器的类型。
萃取迭代器代表的值的类型。
萃取迭代器使用值的引用指针等类型。
这个迭代器实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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; }; 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; }; 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 定义了特殊的萃取类型。
总结
从上面分析,对于C++库,萃取的实现一般都是定义模板来实现,对于普通的类型,匹配这个模板的定义;然后针对特殊类型实现特化模板支持。