(C++模板编程):typelist实现(类型计算的范例)

typelist实现(类型计算的范例)

【基本介绍】

  • 《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
  • 关系图

(C++模板编程):typelist实现(类型计算的范例)_第1张图片

【遍历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
    至此,整个执行结束

 

你可能感兴趣的:(#,C++模板编程)