C++ template meta-programming 一步步实现快速排序

参考链接:https://github.com/MagicBowen/tlp/tree/master/include/tlpC++11 模板元编程 - 目录 - 简书

具体代码实现:https://github.com/pisuto/tinySTLLearning/tree/master/Test/TemplateMetaProgramLearning 

一、数值结构

 在实现快速排序之前,首先是定义存储数据的类型结构。在模板元编程中,每一个值其实是使用一个一个类型表示,那么就需要定义一个类,作为所有数值类型的父类。派生类只要使用父类定义的值和类型即可。

    // num type
    template<typename T, T N>
    struct num_type
    {
        using value_type = T;
        static constexpr T value = N;
    };

    template<int N>
    struct int_type : num_type<int, N> {};

    // bool
    template<bool b>
    struct bool_type : num_type<bool, b> {};

二、基本的操作类

在定义了数值类型之后,对这些数值的操作是必不可少的,类似于函数一样。以数值比较为例子,对于>=的操作是需要通过模板来实现的,因为数值类型本身就是类。另外>大于号好像在模板中不能被区分

    template<typename H, typename T>
    struct type_greater
    {
        // >大于号好像不能很好区分
        using result = bool_type < T::value < H::value>;
    };

    // 尝试实现其他的模板类型
    // type_add     实现数值叠加并获得结果
    // type_is_same 判断两个类型是否一致
    // type_negate  对bool_type类型进行取反
    // ... ...
    //

除了一些数值操作类的函数的实现,也需要实现有if和for语句功能的模板类。这里的if语句使用模板特化来实现,通过true_result和false_result来进行区分。而for循环(放在讲list的时候重点讲解)则是需要通过递归来实现。

    using false_result = bool_type<false>;
    using true_result = bool_type<true>;
    
    // 根据结果选择不同的分支
    template<typename B, typename H, typename T>
    struct type_is_choose;

    template<typename H, typename T>
    struct type_is_choose<true_result, H, T>
    {
        using result = H;
    };

    template<typename H, typename T>
    struct type_is_choose<false_result, H, T>
    {
        using result = T;
    };

对于判断两个类型是否可以互相转化也可以通过模板来实现,这里最主要的是采用函数重载来实现不同类型的区分。“...”表示的是可以输入任意个数的形参。需要注意的是其中的注释部分,如果仅仅是用返回值给bool_type赋值这是有问题的,因为其是在运行时决定的,这里必须是在编译时就决定,因此采用size_of来处理。

    template<typename B, typename D>
    struct type_is_convertible
    {
    //private:
    //    static D self();
    //    static bool test(...) { return false; };
    //    static bool test(B) { return true; };

    //public:
    //    // 这种方法不行是因为返回值是在运行时候决定的
    //    using result = bool_type<test(self())>;

    private:
        using Yes = char;
        struct No { char dummy[2]; };

        static Yes test(B);
        static No test(...);
        static D self();

    public:
        using result = bool_type<sizeof(Yes) == sizeof(test(self()))>;
    };

 类似地,还有type_is_base_of来判断一个类是否是另一个类的基类,那么就可以用type_is_convertible来判断D*是否可以向B*进行转换。需要注意的是D和B不能是同一类型,否则就不符合该类型的意义。另外,两个类的类型不能都是void*。

    template<typename B, typename D>
    struct type_is_base_of
    {
    private:
        using condition_not_same_type = typename type_and<
            typename type_negate<typename type_is_same<B, D>::result>::result,
            typename type_negate<typename type_is_same<void*, D*>::result>::result>::result;
    public:
        using result = typename type_and<condition_not_same_type,
            typename type_is_convertible<B*, D*>::result>::result;

其余还有许多用于操作的类,必须自己学会动手实现。

    // 两个数值类型的值是否相等
    type_is_equal
    // 为bool_type结果取反
    type_negate
    // 为两个bool_type结果取与
    type_and
    // 判断类型是否有效得到bool_type
    type_is_valid
    // 实现>=
    type_greater_equal
    //
    // ... ...

三、使用模板实现LIST 

为了存储一组数据,在常规的操作中是创建一个数组变量,例如new int[5]或者直接使用std::vector。而在模板中要存储变量必须在类型里面进行嵌套,而数值则是作为一个模板参数存在。因此,需要创建一个类型用于连接前后的数据(其实从这里可以看出,相对于list里的元素,这个数据结构更像是单链表里的node),即:

    template<typename H, typename T>
    struct type_elem
    {
        using head = H; // 真正保存的数据
        using tail = T; // 连接后续的数据
    };

    // null
    struct null_type {};

这里的模板参数是一个类型,那么要怎么存储数据呢?这就用到了前面定义的数值类型int_type。

以存储一个数据为例子:type_elem<1, null_type>

以存储两个数据为例子:type_elem<1, type_elem<2, null_type>>

以存储三个数据为例子:type_elem<1, type_elem<2, type_elem<3, null_type>>>

从中可以看到数据是以递归的形式一层嵌套一层存在的,那么和函数里的递归一样需要递归函数和终止条件

    template<typename H, typename... Ts>
    struct type_list
    {
        using result = type_elem<H, typename type_list<Ts...>::result>;
        // static constexpr auto size = sizeof(Ts)...;
    };

    template<typename H>
    struct type_list<H>
    {
        using result = type_elem<H, null_type>;
    };

    // for example
    using list = typename type_list<int_type<2>, int_type<1>, int_type<2>, int_type<0>, int_type<3>>::result;

四、LIST的工具类

 在完成list的定义之后,为了完成快速排序,还需要编写类似于原生list、vector等容器的工具函数一样的工具类。

以push为例子,在list最后添加一个元素,那么就需要遍历list直至末尾,并加上一个type_elem类。同理的append结合两个list也是如此,在最后末尾添加上list2即可。

    // push
    template<typename TL, typename V>
    struct type_push;

    template<typename T>
    struct type_push<null_type, T>
    {
        using result = typename type_elem<T, null_type>;
    };

    template<>
    struct type_push<null_type, null_type>
    {
        using result = null_type;
    };

    template<typename H, typename T, typename V>
    struct type_push<type_elem<H, T>, V>
    {
        using result = type_elem<H, typename type_push<T, V>::result>;
    };

    // append
    template<typename TL1, typename TL2>
    struct type_append;

    template<typename H1, typename TL2>
    struct type_append<type_elem<H1, null_type>, TL2>
    {
        using result = type_elem<H1, TL2>;
    };

    template<typename H1, typename T1, typename TL2>
    struct type_append<type_elem<H1, T1>, TL2>
    {
        using result = typename type_elem<H1, typename type_append<T1, TL2>::result>;
    };

为了实现quick_sort,其中的筛选操作是必不可少的,通过type_filter应用某一规则获得适应该规则的所以元素形成list,丢弃所以不满足规则的元素。这就必须使用二选一的操作类,即前文构建的type_is_choose。

    template<typename TL, typename V, template<typename S1, typename S2> class Pred>
    struct type_filter;

    template<typename V, template<typename S1, typename S2> class Pred>
    struct type_filter<null_type, V, Pred>
    {
        using result = null_type;
    };

    template<typename H, typename T, typename V, template<typename H, typename V> class Pred>
    struct type_filter<type_elem<H, T>, V, Pred>
    {
        using result = typename type_is_choose<typename Pred<H, V>::result,
            type_elem<H, typename type_filter<T, V, Pred>::result>,
            typename type_filter<T, V, Pred>::result>::result;
    };
// 还有许多工具类
//
// 获取list的长度
   type_length
// 获取某一位置的元素
   type_at
// 元素是否被包含
   type_is_included
// 删除某一元素
   type_erase
// 去除重复的元素
   type_unique
// 判断list1是否是list2的子集
   type_is_subset
// ... ...

五、快速排序

 参考连接:别再问我 C++ 里怎么写快速排序(雾) - 知乎

 实际上用函数写快速排序就是一个简单的思想,将数据集以主元为标准将小于主元的数放在主元的左边,将大的数放在主元的右边,然后再在这两数据集中继续递归,即:

 那么就依照以上的思想就可以在模板中实现快速排序。

    template<typename TL>
    struct type_quick_sort;

    template<>
    struct type_quick_sort<null_type>
    {
        using result = null_type;
    };

    template<typename H, typename T>
    struct type_quick_sort<type_elem<H, T>>
    {
        using result = typename type_append<
            typename type_push<typename type_filter<typename type_quick_sort<T>::result, H, type_less>::result, H>::result,
            typename type_filter<typename type_quick_sort<T>::result, H, type_greater_equal>::result
        >::result;
    };

    // 切记不能忘记result
    using quick_sort = typename type_quick_sort<list>::result;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值