目录

前言

经过前两次经验的积累,终于来到了麻烦的堆排序。在一开始接触模板元编程的时候,我就期望有一天能够写出元编程堆排序的代码。原因是看了知乎大佬的一篇文章《在简历上写了“精通 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. charles 常用功能 (六)抓包结果列表指展示关注的接口(focus on 功能)

    添加关注的接口 2.启用关注接口,添加过滤的地址 3.重新抓包结果 不在上一步配置中的接口,都会隐藏在other host中

  2. HTTP协议数据包

    HTTP数据包 简介:由w3c制定的一种网络应用层协议,定义了浏览器与web服务器之间通信时所使用的数据格式. 0x00 数据包格式 1.请求行:请求类型/请求资源路径.协议的版本和类型 2.请求头: ...

  3. 重要消息:MoviePy v2.0.0.dev1预发布版本已经可以下载安装使用

    ☞ ░ 前往老猿Python博文目录 ░ 刚刚得知,MoviePy v2.0.0.dev1版本已经预发布,据说解决了多语言支持及TextClip等一系列Bug,大家不妨升级使用.升级指令:pip in ...

  4. MDX非常规百分比算法-过滤数据后的百分比

    网上有很多关于占比的帖子,基本上都是按照层次结构来做的,比如某个子项占总体的百分比(\all).某个子项占父项的百分比(\parent).某个子项占其祖先的百分比(\ancestor)....等等,如 ...

  5. 再也不怕 JavaScript 报错了,怎么看怎么处理都在这

    在开发中,有时,我们花了几个小时写的 JS 代码,在游览器调试一看,控制台一堆红,瞬间一万头草泥马奔腾而来.至此,本文主要记录 JS 常见的一些报错类型,以及常见的报错信息,分析其报错原因,并给予处理 ...

  6. 一个小时学会用 Go 编写命令行工具

    前言 最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼"真香". 但现阶段相对来说还是 Python 写的 ...

  7. springboot:定时任务

    在日常的开发过程中经常使用到定时任务,在springMVC的开发中,经常和quartz框架进行集成使用,但在springboot中没有这么做,而是使用了java的线程池来实现定时任务. 一.概述 在s ...

  8. 笔记-[ZJOI2014]力

    [ZJOI2014]力 \[\begin{split} E_j=&\sum_{i=1}^{j-1}\frac{q_i}{(i-j)^2}-\sum_{i=j+1}^{n}\frac{q_i}{ ...

  9. Android FART脱壳机流程分析

    本文首发于安全客 链接:https://www.anquanke.com/post/id/219094 0x1 前言 在Android平台上,程序员编写的Java代码最终将被编译成字节码在Androi ...

  10. BIOS、UEFI、Boot Loader都是些什么

    BIOS.UEFI.Boot Loader都是些什么 目录 BIOS.UEFI.Boot Loader都是些什么 什么是BIOS 基本的输入输出是什么 自检程序"检"了什么 系统自 ...