现代C++与模板元编程(5)

如果Head和T不匹配时,我们需要借助Append把Head粘回TypeList,这是在定义那节提到的弊端之一,因为我们不可能直接展开TypeList类型,它不是变长模板的参数包。后面的几个元函数中都需要用到Append来完成相同的工作,与传统的链式实现相比这一点确实不够优雅。

有了Erase,实现EraseAll就简单很多了,我们只需要在终止条件1那里不终止,而是对剩下的list继续进行EraseAll即可:

template <typename TList, typename T> struct EraseAll; template <typename Head, typename... Tails, typename T> struct EraseAll<TypeList<Head, Tails...>, T> { using result_type = typename Append<Head, typename EraseAll<TypeList<Tails...>, T>::result_type >::result_type; }; // 这里不会停止,而是继续把所有匹配的元素删除 template <typename... Tails, typename T> struct EraseAll<TypeList<T, Tails...>, T> { using result_type = typename EraseAll<TypeList<Tails...>, T>::result_type; }; template <typename T> struct EraseAll<TypeList<>, T> { using result_type = TypeList<>; };

有了Erase和EraseAll,下面去除重复元素的元函数也就能实现了。

NoDuplicates去除所有重复type

NoDuplicates也许看起来会很复杂,其实不然。

NoDuplicates算法只需要三步:

先对去除Head之后的TypeList进行NoDuplicates操作,形成L1;现在保证L1里没有重复元素

对L1进行删除所有Head的操作,形成L2,因为L1里可能会有和Head相同的元素;

最后将Head添加回TypeList

步骤1中递归的调用还会重复相同的步骤,这样最后就确保了TypeList中不会有重复的元素出现。这个元函数也是较为常用的,比如你肯定不会想在抽象工厂模板类中出现两个相同的类型,这不正确也没有必要。

调用形式为:

NoDuplicates<TList>::result_type;

按照步骤实现算法也不难:

template <typename TList> struct NoDuplicates; template <> struct NoDuplicates<TypeList<>> { using result_type = TypeList<>; }; template <typename Head, typename... Tails> struct NoDuplicates<TypeList<Head, Tails...>> { private: // 保证L1中没有重复的项目 using L1 = typename NoDuplicates<TypeList<Tails...>>::result_type; // 删除L1中所有和Head相同的项目,L1中已经没有重复,所以最多只会有一项和Head相同,Erase就够了 using L2 = typename Erase<L1, Head>::result_type; public: // 把Head添加回去 using result_type = typename Append<Head, L2>::result_type; };

在处理L1时我们只使用了Erase,注释已经给出了原因。

Replace和ReplaceAll

除了删除,偶尔我们也希望将某些type替换成新的type。

这里我只讲解Replace的实现,Replace和ReplaceAll的区别就像Erase和EraseAll,因此不再赘述。

Replace其实就是翻版的Erase,只不过它并不删除匹配的Head,而是将其替换成了新类型。

template <typename TList, typename Old, typename New> struct Replace; template <typename T, typename U> struct Replace<TypeList<>, T, U> { using result_type = TypeList<>; }; template <typename... Tails, typename T, typename U> struct Replace<TypeList<T, Tails...>, T, U> { using result_type = typename Append<U, TypeList<Tails...>>::result_type; }; template <typename Head, typename... Tails, typename T, typename U> struct Replace<TypeList<Head, Tails...>, T, U> { using result_type = typename Append<Head, typename Replace<TypeList<Tails...>, T, U>::result_type>::result_type; }; Derived2Front将派生类型移动至list前部

前面的元函数基本都是将参数包分解为Head和Tails,然后通过递归依次处理,但是现在描述的算法就有些复杂了。

通过给定一个Base,我们希望TypeList中所有Base的派生类都能出现在list的前部,位置先于Base,这在你处理继承的层次结构时会很有帮助,当然我们后面是示例中没有使用此功能,不过作为一个比较重要的接口,我们还是需要进行一定的了解的。

首先想要将派生类移动到前端就需要先找出在list末尾上的派生类型,我们使用一个帮助类的元函数MostDerived来实现:

template <typename TList, typename Base> struct MostDerived; // 终止条件,找不到任何派生类就返回Base自己 template <typename T> struct MostDerived<TypeList<>, T> { using result_type = T; }; template <typename Head, typename... Tails, typename T> struct MostDerived<TypeList<Head, Tails...>, T> { private: using candidate = typename MostDerived<TypeList<Tails...>, T>::result_type; public: using result_type = std::conditional_t<std::is_base_of_v<candidate, Head>, Head, candidate>; };

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c0c52608c23b37e352041892c54b0e9c.html