栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

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

一、数值结构

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

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

    template
    struct int_type : num_type {};

    // bool
    template
    struct bool_type : num_type {};
二、基本的操作类

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

    template
    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;
    using true_result = bool_type;
    
    // 根据结果选择不同的分支
    template
    struct type_is_choose;

    template
    struct type_is_choose
    {
        using result = H;
    };

    template
    struct type_is_choose
    {
        using result = T;
    };

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

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

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

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

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

    public:
        using result = bool_type;
    };

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

    template
    struct type_is_base_of
    {
    private:
        using condition_not_same_type = typename type_and<
            typename type_negate::result>::result,
            typename type_negate::result>::result>::result;
    public:
        using result = typename type_and::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
    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
    struct type_list
    {
        using result = type_elem::result>;
        // static constexpr auto size = sizeof(Ts)...;
    };

    template
    struct type_list
    {
        using result = type_elem;
    };

    // for example
    using list = typename type_list, 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
    struct type_push;

    template
    struct type_push
    {
        using result = typename type_elem;
    };

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

    template
    struct type_push, V>
    {
        using result = type_elem::result>;
    };

    // append
    template
    struct type_append;

    template
    struct type_append, TL2>
    {
        using result = type_elem;
    };

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

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

    template class Pred>
    struct type_filter;

    template class Pred>
    struct type_filter
    {
        using result = null_type;
    };

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

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

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

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

    template
    struct type_quick_sort;

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

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/296337.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号