有了点模板元编程的traits基础,看STL源码清晰多了,以前看源码的时候总被各种各样的typedef给折腾得看不下去,
将<list>头文件的类继承结构简化如下
#include <xmemory> #include <stdexcept> #define _STD_BEGIN namespace std { #define _STD_END } _STD_BEGIN // 第一个模板参数 template <class _Val_types> class _List_val : public _Container_base { }; // 第二个模板参数 template <class _Ty, class _Alloc0> struct _List_base_types { }; template <bool _Al_has_storage, class _Alloc_types> class _List_alloc : public _List_val<typename _Alloc_types::_Val_types> { }; template <class _Ty, class _Alloc> // 这里默认_Alloc不为空类型 class _List_buy // !is_empty<_Alloc>::value暂不讨论 : public _List_alloc<true, _List_base_types<_Ty, _Alloc>> { }; template <class _Ty, class _Alloc = allocator<_Ty> > class list : public _List_buy<_Ty, _Alloc> { }; _STD_END
举个例子,看看list<int>这个实例化会产生什么效果,从下往上看。
_Ty被替换成int,_Alloc默认被替换成allocator<int>,上一层基类_List_buy的两个模板参数也是_Ty和_Alloc
再上一层基类_List_alloc有2个模板参数,第一个是bool值,编译期判断是否为空类型(empty class),第二个则是由_Ty和_Alloc两个模板参数实例化的_List_base_types类,该类没有基类型,这个类如同名字所说,list的基本类型。
_List_alloc的基类_List_val也是由_List_base_types作为模板参数实例化,_List_val的基类Container_base0仅仅包含两个函数体内为空的函数。
结论:list<>进行实例化后,模板参数_Ty是通过_List_base_types来定义list各类中的类型别名。
现在来看看_List_base_types内部的typedef
template<class _Ty, class _Alloc0> struct _List_base_types { // types needed for a container base typedef _Alloc0 _Alloc; typedef _List_base_types<_Ty, _Alloc> _Myt; typedef _Wrap_alloc<_Alloc> _Alty0; typedef typename _Alty0::template rebind<_Ty>::other _Alty; typedef typename _Get_voidptr<_Alty, typename _Alty::pointer>::type _Voidptr; typedef _List_node<typename _Alty::value_type, _Voidptr> _Node; typedef typename _Alty::template rebind<_Node>::other _Alnod_type; typedef typename _Alnod_type::pointer _Nodeptr; typedef _Nodeptr& _Nodepref; typedef typename _If<_Is_simple_alloc<_Alty>::value, _List_simple_types<typename _Alty::value_type>, _List_iter_types<typename _Alty::value_type, typename _Alty::size_type, typename _Alty::difference_type, typename _Alty::pointer, typename _Alty::const_pointer, typename _Alty::reference, typename _Alty::const_reference, _Nodeptr> >::type _Val_types; };
_Myt为实例化后的该类模板的别名。
先看看最后的关键的_Val_types,变量类型,通过元函数(Meta Function)_If来完成类型计算。
template<bool, class _Ty1, class _Ty2> struct _If { // type is _Ty2 for assumed false typedef _Ty2 type; }; template<class _Ty1, class _Ty2> struct _If<true, _Ty1, _Ty2> { // type is _Ty1 for assumed true typedef _Ty1 type; };
通过模板特化来完成编译期的判断,三个模板参数,第一个为bool,如果第一个参数为true,那么类型type是_Ty1,否则类型type是_Ty2。
_Val_types第一个参数<_Is_simple_alloc<_Alty>::value,判断_Alty是否为“简单的内存分配器”,如果不是,则意味着你专门定制了特殊的内存分配器(类),并将这个类的类型别名(size_type、pointer等等)传递给_Val_types。
如果是,就直接用_List_simple_types,也就是list的简单类型。
template<class _Ty> struct _List_simple_types : public _Simple_types<_Ty> { // wraps types needed by iterators typedef _List_node<_Ty, void *> _Node; typedef _Node *_Nodeptr; };
到这里就可以发现,链表类必须用到的结点类就在这里:_List_node<_Ty, void *>
template<class _Value_type, class _Voidptr> struct _List_node { // list node _Voidptr _Next; // successor node, or first element if head _Voidptr _Prev; // predecessor node, or last element if head _Value_type _Myval; // the stored value, unused if head private: _List_node& operator=(const _List_node&); }; template<class _Value_type> struct _List_node<_Value_type, void *> { // list node typedef _List_node<_Value_type, void *> *_Nodeptr; _Nodeptr _Next; // successor node, or first element if head _Nodeptr _Prev; // predecessor node, or last element if head _Value_type _Myval; // the stored value, unused if head private: _List_node& operator=(const _List_node&); };
结点类包含前向指针和后向指针,双向链表,并且把赋值运算符的重载置为private,禁止了结点间进行赋值。
因为进行赋值如果是简单的引用传递,没有意义,如果新建了个一模一样的结点,链表就不再是链表,而形成了闭合的图结构。
至于它的基类_Simple_types<_Ty>则是一些基本类型的集合
// TEMPLATE CLASS _Simple_types template<class _Value_type> struct _Simple_types { // wraps types needed by iterators typedef _Value_type value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef value_type *pointer; typedef const value_type *const_pointer; typedef value_type& reference; typedef const value_type& const_reference; };
比如_Ty为int的话,_Simple_types里面的类型别名就是
int(值类型)、size_t(尺寸类型)、ptrdiff_t(差数类型)、int*(指针)、const int*(常指针)、int&(引用)、const int&(常饮用)
用了一些通用的接口来实现类型的统一。