【基本介绍】
- 《Modern C++ Design》 ---《C++设计新思维》-- Loki实现了typelist(列表头+嵌套列表技术来实现,因为2003年C++不支持可变参模板)
- typelist:用来操作一大堆类型的C++容器,就象C++标准库中的list容器(能够对各种数值提供各种基本操作),但typelist是针对类型进行操作。
- typelist简单的说就是一个类型容器,能够对类型提供一系列的操作(把类型当成数据来操作)。
- 从实现上,typelist是一个类模板,中文名字叫:类型列表。该类模板用来表示一个列表,在该列表中存放着一堆类型。
【定义typelist泛化版本】
//所有的typelist设计代码都放入这里 template
//T代表typelist中装的一包类型(typelist中装的一系列元素) class typelist { };
- 定义别名,测试用
using TPL_NM1 = typelist
; using TPL_NM2 = typelist<>; //没有元素的typelist容器
【取得typelist中的第一个元素(front)】
//取得typelist中的第一个元素(front) //泛化版本,用不到,所以只需要声明,存在的目的就是要引出特化版本 template
//TPLT代表整个typelist<...>类型 class front; //特化版本,写特化版本时,先书写front后面尖括号中的内容 //(这个内容必须遵从泛化版本中template中的内容来书写,注意泛化版本只有一个参数) //回头再根据尖括号中用到的内容来填补template中的内容 template class front< typelist > { public: using type = FirstElem; };
- 测试
cout << typeid(front
::type).name() << endl; cout << typeid(front ::type).name() << endl; //报错,没实现泛化版本
【取得typelist容器中元素的数量(size)】
//泛化版本 template
class size; //特化版本 template class size< typelist > { public: static inline size_t value = sizeof...(Args); };
- 测试
cout << size
::value << endl; //5 cout << size ::value << endl; //0
【从typelist中移除第一个元素(pop_front)】
//泛化版本 template
class pop_front; //特化版本 template class pop_front< typelist > { public: using type = typelist ; };
- 测试
cout << typeid(pop_front
::type).name() << endl; cout << typeid(pop_front ::type).name() << endl; //报错
【向typelist的开头插入一个元素(push_front)】
//向开头插入元素,泛化版本 template
//TPLT代表整个typelist<...>类型,NewElem代表要插入的元素 class push_front; //向开头插入元素,特化版本 template class push_front< typelist , NewElem> { public: using type = typelist< NewElem, Elem...>; };
- 测试
cout << typeid(push_front
::type).name() << endl; cout << typeid(push_front ::type).name() << endl;
【向typelist的末尾插入一个元素(push_back)】
//向末尾插入元素,泛化版本 template
//TPLT代表整个typelist<...>类型,NewElem代表要插入的元素 class push_back; //向末尾插入元素,特化版本 template class push_back< typelist , NewElem> { public: using type = typelist< Elem..., NewElem>; };
- 测试
cout << typeid(push_back
::type).name() << endl; cout << typeid(push_back ::type).name() << endl;
【替换typelist的开头元素(replace_front)】
//泛化版本 template
class replace_front; //特化版本 template class replace_front< typelist< FirstElem, OtherElem...>, NewElem> { public: using type = typelist ; };
- 测试
cout << typeid(replace_front
::type).name() << endl;
【判断typelist是否为空(is_empty)】
//泛化版本 template
class is_empty { public: static inline const bool value = false; }; //特化版本 template <> class is_empty > { public: static inline const bool value = true; };
- 测试
cout << is_empty< TPL_NM1>::value << endl; cout << is_empty< TPL_NM2>::value << endl;
【根据索引号查找typelist的某个元素(find)】
//泛化版本 template
class find :public find ::type, index_v - 1> { }; //特化版本 template class find :public front //0,作为递归的出口了 { };
- 测试
cout << typeid(find
::type).name() << endl; //cout << typeid(find ::type).name() << endl; //超出边界,编译时就会报错
- 分析
find
::type:函数调用,调用的是find的泛化版本,把TPL_NM1替换一下 //===========>find< typelist , 2> 会被实例化出来。分析一下其父类 find< typelist , 2> 的父类是 find< typelist , 1> find< typelist , 1>的父类是find< typelist , 0>,正好是find的特化版本 find< typelist , 0>的父类 应该是 front< typelist > 因为front(find的父类)的特化版本中定义了type,因此main中find ::type的结果其实也就是find >::type
- 关系图
【遍历typelist找到sizeof值最大的元素(get_maxsize_type)】
- 元编程计算完整:
- 迭代构造(循环构造):也就是在get_maxsize_type模板中融入递归编程技术
- 状态变量:指的是类模板get_maxsize_type中的模板参数,因为这里类模板的模板参数是typelist,所以每次递归,typelist中的元素数量肯定要少一个,最终才能符合递归结束条件。
- 执行路径的选择:使用std::conditional以及get_maxsize_type类模板的特化达到结束递归的效果,这个特化版本的特点是模板参数typelist中的元素为0个。
- 运算对象:类模板中的type成员里面记录的就是当前sizeof值最大的类型。
//泛化版本 template
class get_maxsize_type { private: //当前get_maxsize_type中typelist(模板参数)的第一个元素 using tl_first_elem = typename front ::type; //获取type中第一个元素(是一个类型),比如获取到int using tl_remain = typename pop_front ::type; //递归下去的get_maxsize_type中typelist(模板参数)的第一个元素 using tl_first_elem_rec = typename get_maxsize_type < tl_remain > ::type; public: using type = typename std::conditional < sizeof(tl_first_elem) >= sizeof(tl_first_elem_rec), tl_first_elem, tl_first_elem_rec >::type; }; //特化版本 template <> class get_maxsize_type< typelist<> > { public: using type = char; };
- 调用
cout << typeid(get_maxsize_type
::type).name() << endl; //double
- 调用分析
get_maxsize_type< typelist
>::type tl = int tl__first_elemremain = typelist tl_first_elem_rec = get_maxsize_type< typelist >::type //对type的调用涉及求值:sizeof(tl_first_elem_rec),因此需要先计算tl_first_elem_rec tl_first_elem = double tl_remain = typelist tl_first_elem_rec = get_maxsize_type< typelist >::type tl_first_elem = float tl_remain = typelist tl_first_elem_rec = get_maxsize_type< typelist >::type tl_first_elem = char tl_remain = typelist tl_first_elem_rec = get_maxsize_type< typelist >::type tl_first_elem = bool tl_remain = typelist<> tl_first_elem_rec = get_maxsize_type< typelist<> >::type = char //特化 type = bool//因std::conditional中sizeof值bool>=char,所以type=bool ------------------------------------递归结束,调用流程往回返------------- type = char //因std::conditional中sizeof值char >= bool,所以type=char type = float //因std::conditional中sizeof值float >= char,所以type=float type = double //因std::conditional中sizeof值double >= float,所以type=double type = double //因std::conditional中sizeof值int >= double为假,所以type=double 至此,整个执行结束 【注】
- 在写这种递归程序时,难免遇到障碍,很多情况下会遇到不知道递归代码该如何写的镜框,此时强烈建议:
- 根据自身的理解和认知,先把大致的代码写出来,对于一些不确定的代码行,可以暂时用一些没有语法错误的代码来代替。
- 动手仿照前面的调用关系来整理自己所写的代码的调用关系,通过对这种调用关系的捋顺,反推程序代码该如何写,当调用关系捋顺了,那些不确定怎么样写的代码行也许就能够确定了。
【颠倒(反转)一个typelist中的元素的顺序(reverse)】
- 高级代码书写技巧:写出模糊代码->整理调用关系->反推确定代码。
//泛化版本 template
::value> class reverse; //特化版本1,当typelist中还有元素时 template class reverse { private: using tl_first_elem = typename front ::type; //获取type中第一个元素(是一个类型),比如获取到int //促使递归的using,递归结束后,tl_result_rec负责配合type来整理最终的结果 using tl_result_rec = typename reverse < typename pop_front ::type >::type; public: using type = typename push_back ::type; }; //特化版本2,当typelist中没有元素时 template class reverse { public: using type = typename TPLT; //其实此时TPLT就是typelist<> };
- 测试
cout << typeid(reverse< typelist
>::type).name() << endl;
- 调用分析
reverse
>::type tl_first_elem = int tl_result_rec = reverse >::type; tl_first_elem = double tl_result_rec = reverse >::type; tl_first_elem = float tl_result_rec = reverse >::type; type = typelist<> ------------------------递归结束,调用流程往回返------------- tl_result_rec = typelist<> //tl_result_rec得到了值才能让接着的type得到值 type = push_back , tl_first_elem>::type = typelist tl_result_rec = typelist type = push_back , tl_first_elem>::type = typelist tl_result_rec = typelist type = push_back , tl_first_elem>::type = typelist 至此,整个执行结束