目录

前言

经过前两次经验的积累,终于来到了麻烦的堆排序。在一开始接触模板元编程的时候,我就期望有一天能够写出元编程堆排序的代码。原因是看了知乎大佬的一篇文章《在简历上写了“精通 C++”后……》。由于学识浅薄,感觉只能接触到模板元编程这一部分,所以便开始了对模板元编程的研究。经过多次的学习研究,最终在今天完成了这一成就。但是时间花了三个小时左右,所以时间肯定是不能达标了,但是也算是一个比较奇特的经历吧。

相比较于前两种排序方式,堆排序需要交换操作,更多的判断操作,随机读取等。这一些都不是模板元编程擅长的地方,但是幸运的是都是有办法实现。

实现的一些小细节

Debug

前两次实现的时候,运气很好,结束的时候都没有出现什么bug,但是这一次的比较麻烦,出现了比较严重的bug。但是由于限制,没办法进行很好的调试。于是通过编译器的报错机制,我写了一个元编程的断言。当输入的flag为false时,便会报错。

template<bool>
struct m_assert; template<>
struct m_assert<true> {
typedef int result_type;
}; template<>
struct m_assert<false> {};

使用的时候在需要查看的类里面,放入一个类似的语句,然后将flag改成相应的条件,就可以得到调用栈了。

typename m_assert<m_or<begin != 0, T::size != 3>::result>::result_type non_data;

实现的方法也很简单,就是区分truefalse,当输入为true时,结构体便有result_type类型,而false则没有。此时如果使用其的result_type,便会产生报错,以便观察程序的调用。编译输出为:

但是这一种方法没办法打印当前调用的输出,因此我又增加了一层:

template<bool flag, typename ...T>
struct m_assert_print{
typedef typename m_assert<flag>::result_type result_type;
};

使用方法也类似,如果需要打印当前调用的输出,便将要打印的内容作为T输入即可,譬如:

typename m_assert_print<m_or<begin != 0, T::size != 3>::result, result_type>::result_type non_data;

此时的编译输出为:

可以看到相比较之前的输出,这一次的输出多了一层,而在这一层里,包含了需要打印的内容。

惰性求值

我一开始一直以为C++是没有实现惰性求值的,但是经过一系列的实验,和查找资料,好像C++是存在一定的惰性求值的。譬如

template<>
struct m_assert<false> {}; template<bool flag, typename ...T>
struct m_assert_print{
typedef typename m_assert<flag>::result_type result_type;
}; template<bool flag>
struct ErrorType {
typedef typename m_assert<flag>::result_type result_type;
}; struct Test {
typedef typename m_if<true, ErrorType<true>, ErrorType<false>>::result_type::result_type result_type;
};

如果是没有惰性求值的存在,这一段代码是应该报错的。因为在ErrorType<false>中引用了m_assert<fasle>::result_type。而在上一部分就提到过,应该是会必报错的。

而对于另一个写法

struct Test {
typedef typename m_if<true, ErrorType<true>::result_type, ErrorType<false>::result_type>::result_type result_type;
};

编译期却提示错误,甚至将ErrorType模板修改成

template<bool flag>
struct ErrorType {
typedef typename m_assert<flag>::result_type result1_type;
typedef int result_type;
};

编译器也还是会报错。

通过查阅资料总结的话,编译期对于出现的模板并不会全部的具现化。但是如果使用了其中的类型,便会对其进行具现化[1]。因此,通过这个特性,是有办法设计出一些惰性加载的类,但是由于当时并不知道,所以实现的时候采取了偏特化的方法,防止编译时走向一些奇怪的方向。

简单的来说,就是通过增加一个valid输入,来判断需不需要对这个支线进行下去。如果为fasle,则返回默认值,如果为true,则返回操作后的值,配合m_if,便可以得到想要的结果。

总结

总的来说,模板元编程是C++里面一个比较有趣的部分。由于其晦涩难懂,也成为了许多大佬秀操作的地方。但是随着C++标准的完善,它的难度也在不断的降低。在C++17的时候,大部分的函数都可以使用constexpr来修饰了,这让模板元编程在值运算的时候失去了一部分函数式编程的特点。而C++20中的constraintsconcepts,让模板偏特化有了更大的活力。

源代码:https://gist.github.com/ink19/9fdcca26e89655e8c1b80da56ee04c65

Ref

[1] https://www.jianshu.com/p/f2477d2c19ea

博客原文:https://www.cnblogs.com/ink19/p/cpp_template_heap_sort.html

C++模板元编程----堆排序的更多相关文章

  1. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

  2. C++模板元编程 - 函数重载决议选择工具(不知道起什么好名)完成

    这个还是基于之前实现的那个MultiState,为了实现三种类型“大类”的函数重载决议:所有整数.所有浮点数.字符串,分别将这三种“大类”的数据分配到对应的Converter上. 为此实现了一些方便的 ...

  3. C++模板元编程 - 挖新坑的时候探索到了模板元编程的新玩法

    C++真是一门自由的语言,虽然糖没有C#那么多,但是你想要怎么写,想要实现什么,想要用某种编程范式或者语言特性,它都会提供. 开大数运算类的新坑的时候(又是坑),无意中需要解决一个需求:大数类需要分别 ...

  4. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

  5. c++ 模板元编程的一点体会

    趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...

  6. C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

    本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...

  7. C++模板元编程 - 2 模仿haskell的列表以及相关操作

    这是昨天和今天写的东西,利用C++的可变模板参数包以及包展开,模式匹配的一些东西做的,感觉用typename...比轮子叔那个List<A,List<B, List<C, D> ...

  8. 一道模板元编程题源码解答(replace_type)

    今天有一同学在群上聊到一个比较好玩的题目(本人看书不多,后面才知是<C++模板元编程>第二章里面的一道习题), 我也抱着试一试的态度去完成它, 这道题也体现了c++模板元编程的基础和精髓: ...

  9. effective c++ Item 48 了解模板元编程

    1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在 ...

随机推荐

  1. 从代码角度理解NNLM(A Neural Probabilistic Language Model)

    其框架结构如下所示: 可分为四 个部分: 词嵌入部分 输入 隐含层 输出层 我们要明确任务是通过一个文本序列(分词后的序列)去预测下一个字出现的概率,tensorflow代码如下: 参考:https: ...

  2. moviepy音视频剪辑:与大小相关的视频变换函数详解

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<moviepy音视频剪辑:moviepy中的剪辑基类Clip详解>介绍了剪辑基类的fl.fl_time.fx方法,在<movi ...

  3. 第10.6节 Python包的概念

    一. 引言 在<第10.2节 Python的模块及模块导入>介绍了模块的概念及导入的几个关键点,Python中的模块是一个单个的py文件,当我们开发的项目或功能集由多个文件构成时,我们需要 ...

  4. PyQt(Python+Qt)学习随笔:树型部件QTreeWidget中使用sortItems进行项排序

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 树型部件QTreeWidget中的项可以使用sortItems方法按照指定列进行排序,调用语法: s ...

  5. Hadoop 中HDFS、MapReduce体系结构

    在网络环境方面,作为分布式系统,Hadoop基于TCP/IP进行节点间的通信和传输. 在数据传输方面,广泛应用HTTP实现. 在监控.通知方面,Hadoop等分布式大数据软件则广泛使用异步消息队列等机 ...

  6. Norns.Urd 中的一些设计

    Norns.Urd 是什么? Norns.Urd 是一个基于emit实现动态代理的轻量级AOP框架. 版本基于 netstandard2.0. 所以哪些.net 版本能用你懂的. 完成这个框架的目的主 ...

  7. 深入理解Java虚拟机(七)——类文件结构

    Java的无关性 由于计算机领域中有很多操作系统和硬件平台同时在竞争,所以,很多编程语言的程序设计会与其运行的平台和操作系统产生耦合,这样就大大增加了程序员的工作,为了适应不同的平台,需要修改很多代码 ...

  8. 从隔壁兄弟那爬点网页数据,整理成word文档

    package com.parse_report; import com.report.utils.ParseUrl; /** * Create by JIUN 2018/7/14 */ public ...

  9. axios 封装 跨域 实现 (后端跨域配置白名单)

    1. 始vue化项目 vue init webpack deaxios # 使用脚手架创建项目 deaxios(项目名,随便取得) cd deaxios # 进入项目cnpm install npm ...

  10. Linux下安装mysql-5.7.24

    Mysql数据库的安装对于开发者来说,是我们必然会面对的问题,它的安装过程其实并不复杂,并且网络上的安装教程也非常多,但是对于新手来说,各种不同形式的安装教程,又给新手们带来了要选择哪种方式进行安装的 ...