理解萃取

相信了解过stl源码的人都或多或少知道萃取。萃取可以说是C++中一项很精妙的技巧,不得不佩服stl设计者们的智慧。本文并不打算对stl的萃取进行过多介绍,而是用比较简单的方式呈现笔者对萃取的理解。

萃取能做什么

我们知道stl最重要的两个部分是算法和容器,两者通过迭代器关联起来。stl几乎完全是通过泛型实现的,设计的算法和容器也都是通用的,与具体数据类型无关,而我们知道原生类型与自定义类型是存在明显差异的,其中一点就是自定义类型有构造函数和析构函数。那么在执行拷贝赋值等操作时,如果没有构造函数,可以采用很简单的方式处理,比如memcpy、memset等操作,可以极大的提高效率。stl用通用的库,当然要追求效率上的极致,必然要对原生类型和自定义类型分开处理。对于stl的容器和算法,都用的模板实现,如何才能确定处理的数据类型是原生类型还是自定义类型呢,萃取就能干这事。

一个简单的实现

以下代码是我自己实现的一个简单萃取,原理和stl中的一样,我简化了很多。

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
struct is_true {};
struct is_false {};

template <typename T>
struct traits {
typedef is_false is_raw;
};

// 特化所有原生类型
template <>
struct traits<signed char> {
typedef is_true is_raw;
};

template <>
struct traits<unsigned char> {
typedef is_true is_raw;
};

template <>
struct traits<short> {
typedef is_true is_raw;
};

template <>
struct traits<unsigned short > {
typedef is_true is_raw;
};

template <>
struct traits<int> {
typedef is_true is_raw;
};

template <>
struct traits<unsigned> {
typedef is_true is_raw;
};

template <>
struct traits<long long> {
typedef is_true is_raw;
};

template <>
struct traits<double> {
typedef is_true is_raw;
};

template <>
struct traits<float> {
typedef is_true is_raw;
};

template <>
struct traits<bool> {
typedef is_true is_raw;
};

template <>
struct traits<unsigned long long> {
typedef is_true is_raw;
};

template <>
struct traits<void> {
typedef is_true is_raw;
};

template <>
struct traits<void*> {
typedef is_true is_raw;
};

template <>
struct traits<std::nullptr_t> {
typedef is_true is_raw;
};

// for test
template <typename RawType>
void func(RawType);

template <>
void func(is_false) {
printf("this is not raw type\n");
}

template <>
void func(is_true) {
printf("this is raw type\n");
}

template <typename T>
void DoSomething(T t) {
func(typename traits<T>::is_raw());
}

以上代码通过函数模板的参数推导,来确定模板参数是原生类型还是自定义类型,针对不同的类型进行不同的处理。

总结

有时候真的觉得C++语言功能的丰富多彩,给使用者极大的空间。像萃取这样的技巧,给语言提供了很多可能,当然,萃取的能力远不止我上面写的这一点,不过其他的原理也都类型,都是通过模板的参数推导,将不同的类型对应到不同的特化版本进行处理。