目录

前言

经过前两次经验的积累,终于来到了麻烦的堆排序。在一开始接触模板元编程的时候,我就期望有一天能够写出元编程堆排序的代码。原因是看了知乎大佬的一篇文章《在简历上写了“精通 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. 老猿学5G扫盲贴:中国移动5G融合计费漫游计费架构和路由方案

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt+moviepy音视频剪辑实战 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一. ...

  2. Android基础02

    初识安卓的另一个重要的组件---广播. 1.广播的分类 标准广播:是一种完全异步执行的广播,在广播发出之后,所有的广播 接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言.这 ...

  3. 关于utf-8编码值 [ASIS 2019]Unicorn shop

    0x00 前言 这题拿到之后有点懵,后来看了 网上的 wp 更加懵,网上大多数都是直接说 去 compart 搜thousand,然后找个大于1337 的就可以,至于为什么?基本都没有给出解答.于是乎 ...

  4. 科大讯飞语音合成系统 V5.0绿色便携版

    中文名: 中科大讯飞Interphonic 5.0语音合成系统英文名: Interphonic 5.0版本: 5.0发行时间: 2006年制作发行: 中科大讯飞语言: 简体中文系统简介InterPho ...

  5. nginx 静态化合集(去掉index.php目录)

    访问某域名时,去掉index.php目录时达到效果一样 如:www.test1/index.php/test2跟www.test1/test2效果一致 在vhosts.conf中加重写就可以了 loc ...

  6. Day5 - 06 函数的参数-命名关键字参数

    引子:对于关键字参数,调用时可以传入任意个不受限制的关键字参数,至于到底传入了哪些,就需要在函数内部通过[函数里定义的关键字参数]检查,例子里就是通过otherinfo检查.        >& ...

  7. ES6、ES7、ES8

    ES6 https://es6.ruanyifeng.com/   ES7 1.Array.prototype.includes() includes()作用,是查找一个值在不在数组里,若是存在则返回 ...

  8. C#数据结构-线索化二叉树

    为什么线索化二叉树? 对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上,否则我们只知道后续的左右子树.现在我们充分利用二叉树左右子树的空节点,分别指向当前节点的前驱.后继,便 ...

  9. react第二十单元(react+react-router-dom+redux综合案例2)

    第二十单元(react+react-router-dom+redux综合案例2) #课程目标 #知识点 #授课思路 #案例和作业

  10. jmeter性能测试-高并发分布式部署

    jmeter什么要做分布式部署? jmeter是运行在JVM虚拟机上的,当模拟大量并发时,对运行机器的性能/网络负载会很大. 此时就需要使用jmeter的分布式部署功能,实现多台被控机器同时并发访问被 ...