C++14在标准库里添加了一个很有意思的元函数: std::integer_sequence。并且通过它衍生出了一系列的帮助模板:

std::make_integer_sequence, std::make_index_sequence, std:: index_sequence_for。在新的黑魔法的加持下,它可以帮助我们完成在编译期间获取了一组编译期整数的工作。

接下来请系好安全带,准备发车,和大家聊聊新的黑魔法:std::make_index_sequence

1.what's std::make_index_sequence

1.1 举个栗子

笔者这里先从一个简单的例子展开,先带大家看看std::make_index_sequence是如何使用的。

在C++之中有一个很常见的需求,定义一组编译期间的数组作为常量,并在运行时或者编译时利用这些常量进行计算。现在假如我们需编译期的一组1到4的平方值。你会怎么办呢?

嗯.... 思考一下,可以这些写:

constexpr static size_t const_nums[] = {0, 1, 4, 9, 16};

int main() {
static_assert(const_nums[3] == 9);
}

Bingo, 这个代码肯定是正确的,但是如果4扩展到了20或者100?怎么办呢?

嗯~~,先别着急骂脏话,我们可以用std::make_index_sequencestd::index_sequence来帮助我们实现这个逻辑:

template <size_t ...N>
static constexpr auto square_nums(size_t index, std::index_sequence<N...>) {
constexpr auto nums = std::array{N * N ...};
return nums[index];
} template <size_t N>
constexpr static auto const_nums(size_t index) {
return square_nums(index, std::make_index_sequence<N>{});
} int main() {
static_assert(const_nums<101>(100) == 100 * 100);
}

这代码咋看之下有些吓人,不过没关系,我们来一点点的庖丁解牛的解释清楚它是如何工作的。

1.2 做个解释

我们来拆解一下1.1的代码,首先我们定义了一个constexpr的静态函数const_nums。它通过我们本文的主角std::make_index_sequence来构造了一组0,1,2,3 .... N - 1的一组编译器的可变长度的整数列。(注意,这里调用std::make_index_sequence{}的构造函数没有任何意义,纯粹只是利用了它能够生成编译期整数列的能力。)

接着我们来看squere_num函数,这就是我们实际进行平方计算,并生成编译期静态数组的地方了,它的实现很简单,就是依次展开通过std::make_index_sequence生成的数字,并进行平方计算,最后塞到std::array的构造函数之中进行构造。

2. How std::make_index_sequence

通过一个简单的栗子,大家想必已经见识到这个新的黑魔法的独特之处了。接下来,我们进一步的来剖析它的实现吧。

2.1 std::integer_sequence
template< class T, T... Ints >
class integer_sequence;

要了解std::make_index_sequence是如何工作的,就得先看看它的基础类:std::integer_sequence。由上面的代码看,它很简单,就是一个int类型,加上一组int数字,其实原理就是生成一组T类型的编译期间数字序列。它本质上就是个空类,我们就是要获取这个编译期的数字序列。

2.2 std::index_sequence
template<size_t.. Ints>
using index_sequence = std::integer_sequence<size_t,Ints...>;

通常我们不会直接使用std::integer_sequence,而是通过定义一个size_tstd::integer_sequnece命名为index_sequence

2.3 std::make_index_sequence

这里就是生成了一组数字序列0,1,2,3...N - 1的一组std::index_sequence。然后就可以利用这组序列的数字做任何我们想做的事情了。

那么问题来了,std::make_index_sequence是如何实现的呢?

  • 可以通过元编程,生成N个元函数类,依次生成0到N - 1的序列,感兴趣的话可以参考这个链接
  • 实际是由编译器内部在编译期间实现的,并不是基于现有的元编程的逻辑。

3.std::make_index_sequence与std::tuple

通过第二节的介绍,想必大家应该了解了std::make_index_sequence的实现原理了。接下来将介绍它最为重要的使用场景:与tuple的结合。

现在请大家思考一个问题:如何遍历一个std::tuple。(不能使用C++17的std::apply

这个时候就要再次请出我们今天的主角,使用std::make_index_sequnce和lambda表达式来完成这个工作了。我们来看下面这部分代码:

template <typename Tuple, typename Func, size_t ... N>
void func_call_tuple(const Tuple& t, Func&& func, std::index_sequence<N...>) {
static_cast<void>(std::initializer_list<int>{(func(std::get<N>(t)), 0)...});
} template <typename ... Args, typename Func>
void travel_tuple(const std::tuple<Args...>& t, Func&& func) {
func_call_tuple(t, std::forward<Func>(func), std::make_index_sequence<sizeof...(Args)>{});
} int main() {
auto t = std::make_tuple(1, 4.56, "happen lee");
travel_tuple(t, [](auto&& item) {
std::cout << item << ",";
});
}
  • 这个代码首先定义了一个travel_tuple的函数,并且利用了std::make_index_sequence将tuple类型的参数个数进行了展开,生成了0到N - 1的编译期数字。

  • 接下来我们再利用func_call_tuple函数和展开的编译期数字,依次调用std::get<N>(tuple),并且通过lambda表达式依次的调用,完成了遍历tuple的逻辑。

嗯,标准库表示它也是这样想的,所以C++17利用了std::make_index_sequence实现了std::apply,开启了满屏幕堆满tuple的C++新时代了~~

4.小结

C++14新提供的std::make_index_sequence给了我们在编译期操作tuple提供了更加便利的工具,并且在编译期间的整数列也能够帮助我们实现更多新的黑魔法。

大家可以尝试自己用元编程实现了一个std::make_index_sequence, 笔者觉得这是一个很有意思的挑战。

关于std::make_index_sequence就聊到这里。希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。

5.参考资料

cppreference

make_index_sequence的原理

C++雾中风景16:std::make_index_sequence, 来试一试新的黑魔法吧的更多相关文章

  1. Python编程-从入门到实践 Eric Matthes 著 袁国忠 译 - - 第二章 动手试一试

    因为第一章的动手试一试基本都是探索性的,所以直接进入第二章. # 2.2 动手试一试 # 2_1 简单消息: 将一条消息存储到变量中,再将其打印出来. message = 'python 编程从入门到 ...

  2. 仿造w3school的试一试功能,实现左侧编辑框,右侧效果页面

    转自http://fhqllt.iteye.com/blog/836186 每次想快速测试页面效果的时候,特别是在学习前端代码的时候,就想到W3school的那个试一试功能,一直都是用他们那个在线的版 ...

  3. Python 从入门到实践 试一试 参考代码

    这两天学习Python 看了python从入门到实践的书籍,里面有课后题“试一试” 然后就跟着写了,代码在以下地址,如果需要自取 https://files.cnblogs.com/files/fud ...

  4. Qt 学习之路 2(16):深入 Qt5 信号槽新语法

    Qt 学习之路 2(16):深入 Qt5 信号槽新语法  豆子  2012年9月19日  Qt 学习之路 2  53条评论 在前面的章节(信号槽和自定义信号槽)中,我们详细介绍了有关 Qt 5 的信号 ...

  5. 简单的试了试async和await处理异步的方式

    今天无意中就来试了试,感觉这个新的方法还是非常行的通的,接下来我们上代码 这段代码想都不用想输出顺序肯定是//null null 233,当然出现这个问题还是因为它是同步,接下来我们就进行异步方式来处 ...

  6. 试一试 GraphQL

    GraphQL 简介 一种用于 API 的查询语言. GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时. GraphQL 对你的 API 中的数据提供了一套易于理解的完整 ...

  7. 大众点评试水O2O新模式:实体店试穿,扫描二维码付款 现场取货

    在餐饮美食行业取得不错的成绩之后,大众点评将触角延伸到了线下的传统商铺,开始涉足线下商品的 O2O 团购.和传统的线上下单,线下消费的 O2O 模式不同.大众点评的 O2O 团购用户,可在店内试穿后通 ...

  8. Python自学:第二章 动手试一试

    print(1 + 7) print(16 - 8) print(2 * 4) print(8 / 1) 输出为: 8 8 8 8.0 message = " print("I l ...

  9. 试来试去,WIN下最简单的WIN API开发工具,Pelles C就好啦

    昨晚试过N个,不是太大,就是不容易和WIN API集成. 今早一试就灵了个.... Pelles C. Pelles C是一款windows下的C IDE,支持调试,且为免费.它有一个高效率的链接器, ...

随机推荐

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

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

  2. Linux之centos包管理【rpm】、【yum】、【tar】

    rpm包是二进制格式,无需编译安装便可使用,tar包是源码格式,需要编译安装才可使用 rpm包管理: rpm:redhat package manager,红帽的包管理器,其主要的操作参数有如下: - ...

  3. 老猿学5G:融合计费基于QoS流计费QBC的触发器Triggers

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 SMF中的功能体CTF在用户上网时达到一定条件就会向CHF上报流量,而CTF什么时候触发流量上报是由CTF中的触发器来控制的.在<老猿学5G: ...

  4. PyQt(Python+Qt)学习随笔:自定义信号在emit发射信号时报错:AttributeError: object has no attribute

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 如果使用自定义信号,一定要记得信号是类变量,必须在类中定义,不能在实例 ...

  5. Python中import模块时报SyntaxError: (unicode error)utf-8 codec can not decode 错误的解决办法

    老猿有个通过UE编辑(其他文本编辑器一样有类似问题)的bmi.py文件,在Python Idle环境打开文件执行时没有问题,但import时报错: SyntaxError: (unicode erro ...

  6. PyQt学习随笔:截获窗口Widget组件的关闭事件

    在PyQt中,QWidget类对应基础的窗口组件,如果要在窗口组件关闭时截获关闭事件,提供自己的控制机制,则可以通过在自定义的派生类中重写closeEvent方法. 重写closeEvent方法的语法 ...

  7. 建立SQL Server用户登录

    第一步:右击服务器名,点击"属性",选择"安全性",选中"Sql server和Windows身份验证模式". 第二步:鼠标右键点击安全性中 ...

  8. pytorch实战(二)hw2——预测收入是否高于50000,分类问题

    代码和ppt: https://github.com/Iallen520/lhy_DL_Hw 遇到的一些细节问题: 1. X_train文件不带后缀名csv,所以不是规范的csv文件,不能直接用pd. ...

  9. uni与小程序,vue的区别

    标签区别 uni使用小程序的标签,vue使用web端的标签 标签名变化的: 标签描述\类别 vue uniapp 文本 span\font text 链接 a navigator/ router-li ...

  10. Panda 交易所视点观察!区块链金融应用迎新规,哪些版块受影响?

    Panda交易所获悉,近日央行下发推动<区块链技术规范应用的通知>(以下简称"通知")及<区块链技术金融应用评估规则>(以下简称"规则" ...