第五章–5.3.1–基本方法

Aki 发布于 2022-12-02 233 次阅读


一般的,需要对列表进行长度查询,在头尾添加一些元素,将列表转换成其他模板类等。


template<typename... Ts>
struct TypeList {
	struct IsTypeList{};   //标记该类型是一个TypeList,用于定义concept
	using type = TypeList;  //约定使用type成员输出结果,即本身
	constexpr static size_t size = sizeof...(Ts);  //列表的长度

	//成员元函数,在列表尾部添加元素
	template<class ...T>
	using append = TypeList<Ts..., T...>;

	//成员元函数,用于在列表头部添加元素
	template<class ...T>
	using prepend = TypeList<T..., Ts...>;

	//成员元函数,将该列表转换成其他模板类
	template<template<class...>class T>
	using to = T<Ts...>;

};

template<class TypeList>
concept TL = requires{
	typename TypeList::IsTypeList;    //通过特征判断
	typename TypeList::type;     //返回类型
};



using AList = TypeList<int, char>;
static_assert(TL<AList>);
static_assert(AList::size == 2);
static_assert(same_as<AList::append<int, double>, TypeList<int, char, int, double>>);
static_assert(same_as < AList::prepend<double>, TypeList<double, int, char>>);
static_assert(same_as<AList::to<tuple>, tuple<int, char>>);

值得注意的是to方法的输入并不是一个简单的类型,而是另一个接受该列表中所有元素的元函数,从而实现类型转换的效果。to的存在也意味着TypeList只是模板元编程的中间过程,所以不直接使用标准库的tuple等模板类替代TypeList的实现,是因为前者对于编译器而言需要做太多的工作,而TypeList简单纯粹,性能非常高。

剩下的几个元函数都是通过简单的using别名的方式实现的,而不是通过定义模板类的方式,这是因为别名可以减少编译器需要实例化类型的数量,从而提高编译速度。当然别名也不是万能的,它不支持自引用特化,对于这种情况依旧通过模板类的方式实现。

使用静态断言对列表进行代码测试。

5.3.2、高阶函数

有了复合数据结构TypeList之后,我们可以进一步定义一些操作列表的函数。为了减少定义元函数的工作量,同时最大化复用代码,我们可以使用高阶函数。如果一个函数的输入或者输入为函数,那么这个函数就是高阶函数。在过程式编程中,比较常见的函数是sort,它接受一个比较函数作为输入参数。

高阶函数最大的优势在于可以灵活的与其他函数进行组合,借此来实现所需要的功能。

1、map高阶函数

高阶函数map接受一个TypeList和一个单参元函数F,对列表中的每个元素进行迭代并交由F调用,得到迭代后的TypeList。

template<TL ln,template<class>class F>
struct Map;     //元函数,接受一个TypeList和一个单参元函数

template<template<typename>class F,class...Ts>   //元函数实现
struct Map<TypeList<Ts...>,F>:TypeList<typename F<Ts>::type...> {};    //TL每个参数都经由F调用

template<TL ln, template<typename>class F> 
using Map_t = typename Map<ln, F>::type;

Map元函数的实现需要通过偏特化获取TypeList中的各个参数,然后通过参数包展开的方式对每个参数F进行调用。根据标准库的约定,为了方便调用,一般需要定义一个后缀为_t的别名。

另外,根据约定,Map返回的结果需要保存到数据成员type里,而TypeList正好定义了type成员,使用继承特性来继承type可以精简代码。继承特性是模板元编程的常用手段。

使用静态断言测试如下,对每个元素添加指针操作。

using LongList = TypeList<char, double, float, int>;
static_assert(same_as< Map_t<LongList, add_pointer>, TypeList<char*, double*, float*, int*>>);