C++ type traits分析

前言

       我们在平时常常会听到有人说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
{ // 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);
}
};

       这个的主要核心是定义了一个静态常量值:

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

demo

Composite type categories

demo

Type properties

demo

Type features

demo

Type relationships

demo

Property queries

demo

Type transformations

demo

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
// 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 struct _Is_integral : false_type 通用的模板,这个模板中有一个bool value = false的静态成员。
       然后就是真的所有的整数类型,创建特化模块,例如如下:

1
2
3
4
5
template<>
struct _Is_integral<int>
: true_type
{ // determine whether _Ty is integral
};

       这个模板中有一个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)>
{ // 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_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;
};
/// 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 定义了特殊的萃取类型。

总结

       从上面分析,对于C++库,萃取的实现一般都是定义模板来实现,对于普通的类型,匹配这个模板的定义;然后针对特殊类型实现特化模板支持。

文章目录
  1. 1. 前言
  2. 2. integral_constant
  3. 3. C++库的type traits
    1. 3.1. Primary type categories
    2. 3.2. Composite type categories
    3. 3.3. Type properties
    4. 3.4. Type features
    5. 3.5. Type relationships
    6. 3.6. Property queries
    7. 3.7. Type transformations
  4. 4. type traits的例子
  5. 5. type tratis的实现
    1. 5.1. std::is_integral
    2. 5.2. std::is_pod
  6. 6. iterator_traits
  7. 7. 总结