【元编程】typelist的实现

一、typelisttypelisttypelist的介绍

typelisttypelisttypelist这个概念比较系统的出现是在Modern C++ DesignModern \ C++ \ DesignModern C++ Design一书中,关于typelisttypelisttypelist的解释,就是用来操作一大堆类型的C++C++C++容器,就像std::list一样能够为数值提供各自操作一样,不过这里操作的是类型,而不是数值。

这里,基于C++11C++11C++11以上的标准实现typelisttypelisttypelist的部分接口。


二、typelisttypelisttypelist的实现

这里我们将所有的操作都放入tpltpltpl这个命名空间,防止命名污染。


2.1 typelist的定义

首先,typelisttypelisttypelist的定义十分简单,如下,就是一个可变参变量类模板:

	template<typename ... T>
	class typelist {

	};

为了方便操作,我们使用类型别名来创建两个typelist,如下:

using TPL_NUM1 = tplt::typelist<int, double, float, char, bool>;
using TPL_NUM2 = tplt::typelist<>;

2.2 查看typelist的第一个元素

就是在特化版本上,将可变参分离,萃取出第一个元素即可:

//获取typelist的第一个元素

//泛化版本	
template<typename TPLT>
class front;

//特化版本
template<typename FirstElem, typename ... OtherElem>
class front<typelist<FirstElem, OtherElem...>> {
public:
	using type = FirstElem;
};

void Test1() {
	std::cout << typeid(TPL_NUM1).name() << "第一个元素类型为:" << typeid(tplt::front<TPL_NUM1>::type).name() << std::endl;
	
	//如果typelist为空,则会编译失败
	
	//std::cout << typeid(TPL_NUM2).name() << "第一个元素类型为:" << typeid(tplt::front<TPL_NUM2>::type).name() << std::endl;
}

如果为空的typelisttypelisttypelist,则会编译失败,运行结果如下:

在这里插入图片描述

2.3 获取typelist的元素个数

这个接口直接使用sizeof ...()操作符即可,设置valuevaluevaluestatic inline constexpr修饰:

//获取typelist的元素个数

//特化
template<typename TPLT>
class size;

//泛化
template<typename ...Args>
class size<typelist<Args...>> {
public:
	static inline constexpr size_t value = sizeof...(Args);
};

void Test2() {
	std::cout << "typelist1的大小为:" << tplt::size<TPL_NUM1>::value << std::endl;
	std::cout << "typelist2的大小为:" << tplt::size<TPL_NUM2>::value << std::endl;
}

运行结果如下:

在这里插入图片描述

2.4 移除typelist第一个元素

同样也是利用特化版本,分离第一个参数和剩下的参数,用剩下参数构造出一个typelist即可:

//移除第一个元素

//泛化版本
template<typename TPLT>
class pop_front;

//特化版本
template<typename FirstElem,typename... OtherElem>
class pop_front < typelist < FirstElem, OtherElem... >> {
public:
	using type = typelist<OtherElem...>;
};

void Test3() {
	std::cout << "移除第一个元素后类型为:" << typeid(tplt::pop_front<TPL_NUM1>::type).name() << std::endl;

		
	//如果没有元素,则会编译失败
	//std::cout << "移除第一个元素后类型为:" << typeid(tplt::pop_front<TPL_NUM2>::type).name() << std::endl;
}

运行结果如下:

在这里插入图片描述

2.5 向typelist的开头和结尾插入一个新的元素

push_frontpush_back的接口实现类似,只是在分离参数的时候,将新的参数置于第一个和第二个:

//向开头插入元素

//泛化版本
template<typename TPLT, typename NewElem>
class push_front;

//特化版本
template<typename... Elems,typename NewElem>
class push_front<typelist<Elems...>, NewElem> {
public:
	using type = typelist<NewElem, Elems...>;
};

//向结尾插入元素

//泛化版本
template<typename TPLT,typename NewElem>
class push_back;

//特化版本
template<typename... Elems, typename NewElem>
class push_back <typelist <Elems...>, NewElem> {
public:
	using type = typelist < Elems..., NewElem>;
};

void Test4() {
	std::cout << "typelist1向前插入第一个元素后类型为:" << typeid(tplt::push_front<TPL_NUM1,__int64>::type).name() << std::endl;
	
	std::cout << "typelist2向前插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM2, __int64>::type).name() << std::endl;

	std::cout << "typelist1向后插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM1, __int64>::type).name() << std::endl;

	std::cout << "typelist2向后插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM2, __int64>::type).name() << std::endl;
}

运行结果如下:

在这里插入图片描述

2.6 替换typelist的第一个元素

在特化版本分离可变参的时候,将第一个参数替换为新的参数即可:

//替换开头元素

//泛化版本
template<typename TPLT,typename NewElem>
class replace_front;

//特化版本
template<typename FirstElem,typename... OtherElems,typename NewElem >
class replace_front<typelist<FirstElem, OtherElems...>, NewElem> {
public:
	using type = typelist<NewElem, OtherElems...>;
};

void Test5() {
	std::cout << "typelist1替换第一个元素后类型为:" << typeid(tplt::replace_front<TPL_NUM1, __int64>::type).name() << std::endl;

	//如果没有元素,则会编译失败
	//std::cout << "typelist2替换第一个元素后类型为:" << typeid(tplt::replace_back<TPL_NUM2, __int64>::type).name() << std::endl;
}

结果如下:
在这里插入图片描述

2.7 判断typelist是否为空

可以直接继承std::true_typstd::false_type或者自己内部实现一个valuevaluevalue,这里采用前者。

对空的typelisttypelisttypelist实现一个特化,继承std::true_type即可。而泛化版本直接继承std::false_type

代码如下:

//判断是否为空

//泛化版本
template<typename TPLT>
class is_empty :public std::false_type {

};

//特化版本
template<>
class is_empty<typelist<>> :public std::true_type {

};

void Test6() {
	std::cout << "typelist1是否为空:" << tplt::is_empty<TPL_NUM1>::value << std::endl;
	std::cout << "typelist2是否为空:" << tplt::is_empty<TPL_NUM2>::value << std::endl;
}

运行结果如下:

在这里插入图片描述

2.8 随机访问typelist的元素

我们直接继承之前写的front类模板,然后每次就能取出第一个元素,使用递归的方式了,传入一个indexindexindex,每次index−1index-1index1

设置一个特化版本,index=0index = 0index=0,此时去除的第一个元素就是索引位置的元素了:

	//根据索引号查找某个元素

	//泛化版本
	template<typename TPLT,size_t index_v>
	class find :public find<typename pop_front<TPLT>::type, index_v - 1> {

	};
	
	//特化版本
	template<typename TPLT>
	class find<TPLT, 0> :public front<TPLT> {
	
	};

void Test7() {
	std::cout << "typelist1[0] = " << typeid(tplt::find<TPL_NUM1, 0>::type).name() << std::endl;
	std::cout << "typelist1[1] = " << typeid(tplt::find<TPL_NUM1, 1>::type).name() << std::endl;
	std::cout << "typelist1[2] = " << typeid(tplt::find<TPL_NUM1, 2>::type).name() << std::endl;
	std::cout << "typelist1[3] = " << typeid(tplt::find<TPL_NUM1, 3>::type).name() << std::endl;
	
	//如果不存在,则会编译失败
#if 0	
	std::cout << "typelist1[4] = " << typeid(tplt::find<TPL_NUM1, 4::type).name() << std::endl;
	std::cout << "typelist2[0] = " << typeid(tplt::find<TPL_NUM2, 0>::type).name() << std::endl;
#endif
}

结果如下:

在这里插入图片描述

2.9 获取typelist中大小最大的类型

这个接口的实现就比较复杂了,需要使用到std::conditional条件分支,使用sizeof比大小。

在类内部,还需要分离第一个元素和剩余的元素,在剩余的元素还需要分离出第一个元素进行比大小,这样递归下去。

最后实现一个空的typelist特化版本,作为递归的出口。特化版本的类型为charcharchar类型,因为charcharchar类型占一字节,为大小最小的类型。

整体代码如下:


	//获取大小最大的类型

	//泛化版本
	template<typename TPLT>
	class get_maxsize_type {
	private:
		using tl_first_elem = typename front<TPLT>::type;

		using tl_remain = typename pop_front<TPLT>::type;

		//递归下去的第一个元素
		using tl_first_elem_rec = typename get_maxsize_type<tl_remain>::type;
	
	public:
		using type = typename typename std::conditional_t<
			sizeof(tl_first_elem) >= sizeof(tl_first_elem_rec), tl_first_elem, tl_first_elem_rec>;
	};

	//特化版本
	template<>
	class get_maxsize_type <typelist<>> {
	public:
		using type = char; //一个字节,大小最小的类型
	};

2.10 反转typelist的容器序列

反转序列实际上可以看作是,取出第一个元素,插入到后面,不断递归下去,最终得到的typelist就是反转的typelist了。

需要有两个特化版本,其中一个为typelist为空的时候,此时返回空的typelist即可,作为递归的出口。
另一个特化版本是当typelist不为空的时候,此时需要促使递归的进行,不断取出第一个元素,并放入最后

所以还需要设置boolboolbool 参数为is_empty类模板的valuevaluevalue成员,判断是否为空:

//反转typelist的元素顺序

//泛化版本
template<typename TPLT,bool = is_empty<TPLT>::value>
class reverse;

//特化版本1,当typelist存在元素时
template<typename TPLT>
class reverse <TPLT, false> {
private:
	using tl_first_elem = typename front<TPLT>::type;

	//促使递归的using
	using tl_result_elem_rec = typename reverse<typename pop_front<TPLT>::type>::type;

public:
	using type = typename push_back<tl_result_elem_rec, tl_first_elem>::type;

};

//特化版本2,用于typelist为空
template<typename TPLT>
class reverse<TPLT, true> {
public:
	using type = typename TPLT;
};

void Test9() {
	std::cout << "typelist1反转前类型为:" << typeid(TPL_NUM1).name() << std::endl;

	std::cout << "typelist1反转后类型为:" << typeid(tplt::reverse<TPL_NUM1>::type).name() << std::endl;

	//空元素反转前后不变

	std::cout << "typelist2反转前类型为:" << typeid(TPL_NUM2).name() << std::endl;

	std::cout << "typelist2反转后类型为:" << typeid(tplt::reverse<TPL_NUM2>::type).name() << std::endl;
}

运行结果如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值