C++14之std::index_sequence和std::make_index_sequence

相关文章系列

std::apply源码分析

C++之std::tuple(一) : 使用精讲(全)

目录

1.std::integer_sequence

2.std::index_sequence

3.std::make_index_sequence

4.运用

4.1.打印序列的值

4.2.编译时求值

4.3.std::tuple访问值

5.总结


1.std::integer_sequence

        运行时定义一个随机序列比较简单,那么编译时怎么表示一个随机序列怎么办呢?于是std::interger_sequence产生了,它的作用就是表示一个integral数据的序列,这里的integral数据代表std::is_integral为true的数据类型,它主要包括下图列举的一些数据:

std::interger_sequence的定义如下:

template <class _Ty, _Ty... _Vals>
struct integer_sequence { // sequence of integer parameters
    static_assert(is_integral_v<_Ty>, "integer_sequence<T, I...> requires T to be an integral type.");

    using value_type = _Ty;

    _NODISCARD static constexpr size_t size() noexcept {
        return sizeof...(_Vals);
    }
};

通过static_assert和std::is_integral_v限制数据的类型为integral,  通过constexpr编译时求序列的大小。

2.std::index_sequence

它定义如下:

template <size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;

它是一个类模板,表示数据类型为std::size_t的一个序列。它在模板元编程中用于编译时迭代。

3.std::make_index_sequence

它的定义如下:

template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;

template <size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;

template <class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;

   从中可以看出std::make_index_sequence是一个模板别名,它生成一个std::index_sequence类型的对象,该对象包含一系列递增的整数。这个工具在编译时生成一系列的索引,常常用于元编程和编译时计算。

        这里,std::size_t  _size是一个模板参数,表示生成的序列的大小。std::make_integer_sequence是一个模板,它生成一个包含从0到N-1的整数序列的类型。std::index_sequence_for则是一个模板别名,它根据给定的类型生成一个std::index_sequence。

        在实际编程中,我们通常使用std::make_index_sequence来生成一个索引序列,然后使用这个序列来访问元组或数组的元素。这样,我们可以在编译时生成代码,而不需要在运行时进行循环。

        std::make_index_sequence的实现依赖于C++的模板元编程。它使用了递归模板和特化来生成一个包含递增整数的序列。

        以下是一个简化的std::make_index_sequence的实现:

template<std::size_t... Ints>
struct index_sequence {
};

template<std::size_t N, std::size_t... Ints>
struct make_index_sequence_helper : make_index_sequence_helper<N - 1, N - 1, Ints...> {
};

template<std::size_t... Ints>
struct make_index_sequence_helper<0, Ints...> {
    using type = index_sequence<Ints...>;
};

template<std::size_t N>
using make_index_sequence = typename make_index_sequence_helper<N>::type;

        在这个实现中,make_index_sequence_helper是一个模板,它递归地生成一个包含递增整数的序列。当N为0时,递归结束,生成的序列被包装在index_sequence中。

4.运用

4.1.打印序列的值

代码如下所示:

#include <array>
#include <iostream>
#include <tuple>
#include <utility>

template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq) {
  std::cout << "The sequence of size " << int_seq.size() << ": ";
  ((std::cout << ints << ' '), ...);
  std::cout << '\n';
}

// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>) {
  return std::make_tuple(a[I]...);
}

template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a) {
  return a2t_impl(a, Indices{});
}

// 漂亮地打印 tuple
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>) {
  ((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}

template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t) {
  os << "(";
  print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
  return os << ")";
}

int main() {
  print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
  print_sequence(std::make_integer_sequence<int, 20>{});
  print_sequence(std::make_index_sequence<10>{});
  print_sequence(std::index_sequence_for<float, std::iostream, char>{});

  std::array<int, 4> array = {1, 2, 3, 4};

  // 转换 array 为 tuple
  auto tuple = a2t(array);
  static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");

  // 打印到 cout
  std::cout << tuple << '\n';
}

输出:

The sequence of size 7: 9 2 5 1 9 1 6
The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
The sequence of size 3: 0 1 2
(1, 2, 3, 4)

在上面的例子中,我们定义了一个 print_sequence函数,它接受一个序列作为参数并打印其内容。我们通过std::make_index_sequence用不同的方法产生了多个序列并都能正确打印其值。

4.2.编译时求值

现在假如我们需编译期的一组1到4的平方值。你会怎么办呢?思考一下,可以这些写:

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

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

这个代码肯定是正确的,但是如果4扩展到了20或者100?怎么办呢?我们可以用std::make_index_sequence和std::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); 
    return 0;
}

        首先我们定义了一个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的构造函数之中进行构造。

std::make_index_sequence 本身不执行任何操作;它只是生成一个整数序列。实际的遍历和操作通常在一个辅助函数中完成,该函数接受整数序列作为参数。

4.3.std::tuple访问值

        之前在 C++之std::tuple(一) : 使用精讲(全) 中讲解了用 递归遍历元素和 std::apply 方式来访问std::tuple的元素值,这里我们来讲一下用std::index_sequence和std::make_index_sequence来访问std::tuple的元素值。示例代码如下:

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 visitTuple(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(155, false, 566.90, "hello world!");
    visitTuple(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::apply也利用std::make_index_sequence实现了std::apply,详情请查看std::apply源码分析_{ return std::forward<_fn>(__f)(std::forward<_args-CSDN博客

5.总结

   std::index_sequence和std::make_index_sequence结合使用这种方法是模板元编程中的一个常见技巧,它允许我们在编译时生成和执行复杂的操作,而不需要在运行时付出任何额外的性能开销。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/449813.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux:进程

进程 知识铺垫冯诺依曼体系结构操作系统&#xff08;OS&#xff09; 进程概念进程的查看ps 命令获取进程 pid文件内查看进程终止进程的方式kill命令快捷键 进程的创建 forkfork 返回值问题 进程状态运行状态 &#xff1a;R休眠状态&#xff1a;S &#xff08;可中断&#xff09…

【3GPP】【核心网】【5G】5G核心网组网方案(超详细)

5G NR RRC协议总体介绍 UE入网过程包括几个子过程&#xff1a; UE分为三种状态&#xff1a;空闲态&#xff0c;连接态和非活动态。 开机入网流程 小区搜索与选择 UE开机选网&#xff0c;小区搜索并完成下行同步。 系统消息广播 UE读取广播信息&#x…

VMD + CEEMDAN 二次分解,CNN-LSTM预测模型

目录 往期精彩内容&#xff1a; 前言 1 二次分解与数据集制作 1.1 导入数据 1.2 VMD分解 1.3 样本熵 1.4 CEEMDAN分解 1.5 数据集制作 2 基于Pytorch的 CNN-LSTM 预测模型 2.1 定义CNN-LSTM预测模型 2.2 设置参数&#xff0c;训练模型 3 模型评估与可视化 3.1 结果…

学习JavaEE的日子 Day26 手撕所有集合类底层源码

Day26 1.手撕ArrayList底层源码 思路&#xff1a; 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程 public class Test01 { public static void main(String[] args) {//ArrayList<String> list new ArrayList<>();…

【Java从发入门到精通】Java StringBuffer 和 StringBuilder 类

Java StringBuffer 和 StringBuilder 类 当对字符串进行修改的时候&#xff0c;需要使用 StringBuffer 和 StringBuilder 类。 和 String 类不同的是&#xff0c;StringBuffer 和 StringBuilder 类的对象能够被多次的修改&#xff0c;并且不产生新的未使用对象。 在使用 Stri…

在线安装MySQL5.7

在线安装MySQL 安装MySQL5.7 yum -y install mysql57-community-release-el7-10.noarch.rpm 若无可用安装包&#xff0c;执行下面这句 wget http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm 本地安装 yum localinstall -y mysql57-community-releas…

Head First Design Patterns - 命令模式

什么是命令模式 命令模式&#xff0c;把请求封装成对象&#xff0c;以便使用不同的请求、队列或者日志请求来参数化其他对象&#xff0c;并支持可撤回的操作。 为什么会有命令模式 假设要设置一个遥控器&#xff0c;遥控器需要控制多个设备&#xff0c;每个设备除了开关&#…

MS30517SA单通道、高速、低侧栅极驱动器

产品简述 MS30517SA 是单通道、高速、低侧栅极驱 动器器件&#xff0c;能够有效地驱动 MOSFET 和 IGBT 开 关 。 芯片的 设 计 能 够 大 大 减 少 击 穿 电 流 &#xff0c; MS30517SA 能够提供高峰值拉、灌电流脉冲&#xff0c; 同时提供了轨到轨驱动能力以及低传播…

C break 语句

C 语言中 break 语句有以下两种用法&#xff1a; 当 break 语句出现在一个循环内时&#xff0c;循环会立即终止&#xff0c;且程序流将继续执行紧接着循环的下一条语句。它可用于终止 switch 语句中的一个 case。 如果您使用的是嵌套循环&#xff08;即一个循环内嵌套另一个循…

自动驾驶---Motion Planning之构建SLT Driving Corridor

1 背景 在上篇博客《自动驾驶---Motion Planning之Speed Boundary》中,主要介绍了Apollo中Speed Boundary的一些内容,可以构造ST图得到边界信息,最后结合粗糙的速度曲线和路径曲线,即可使用优化的方法求解得到最终的轨迹信息(s,s,s,l,l,l)。 本篇博客笔者主要介绍近…

Matlab 字符串相关命令

参考 字符串命令汇总1

代码随想录算法训练营第四十一天|卡码网46. 携带研究材料(第六期模拟笔试)、416. 分割等和子集

卡码网46. 携带研究材料&#xff08;第六期模拟笔试&#xff09; 刷题https://kamacoder.com/problempage.php?pid1046 题解&#xff1a; 一维背包 文章讲解https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-2.html…

js导出的excel文件无法打开/打开乱码,excel无法打开xxx.xlsx因为文件格式或文件扩展无效

excel无法打开xxx.xlsx因为文件格式或文件扩展无效 使用 a 标签导出这里就不细说了&#xff0c;直接说上述问题解决方案 在调用导出接口的时候加上两个参数 responseType: “blob” responseEncoding: “utf8” export function test(data) {return util({url: /test,method: …

制冷系统管道焊接气焊安全操作

气焊操作安全教育&#xff1a; 1、检查气焊用具完好牢固无损&#xff0c;不得贴粘有&#xff08;机油&#xff09;&#xff1b; 2、气瓶余压&#xff08;2KG&#xff09;停止使用&#xff1b;清除动火 10 米范围内易燃易爆物料&#xff1b; 3、库房内动火要 做好通风排气&…

数据结构——二叉树的遍历【前序、中序、后序】

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

【精选】30+Redis面试题整理(2024)附答案

目录 前言Redis基础项目有用到redis吗&#xff1f;你们项目为什么用redis?redis为什么这么快&#xff1f;了解Redis的线程模型吗&#xff1f;Redis优缺点?redis如何实现持久化&#xff1f;RDB持久化过程&#xff1f;AOF持久化过程&#xff1f;AOF持久化会出现阻塞吗&#xff…

svn安装与汉化(svn1.14.5版本)带汉化包

安装 下载 TortoiseSVN 去官网下载 TortoiseSVN&#xff0c;找到页面底部 TortoiseSVN 下载&#xff0c;选择适用自己电脑位数的 TortoiseSVN 客户端下载&#xff1a; 或者 链接&#xff1a;https://pan.baidu.com/s/1YSw9LalULsQecOQgFrXGSQ?pwd4ds7 提取码&#xff1a;4ds…

套接字编程 --- 三

目录 1. 前置性知识 1.1. listen 系统调用 1.2. accept 系统调用 1.3. 如何通信 1.3.1. read 系统调用 && write系统调用 1.3.2. recv 系统调用 && send 系统调用 2. TCP --- demo 2.1. Tcp_Server.hpp (version 1) 2.2. Tcp_Server.hpp (version 2…

【存储】ZYNQ+NVMe小型化全国产存储解决方案

文章目录 1、背景2、基础理论3、设计方案3.1、FPGA设计方案3.1.1、NVMe控制器实现3.1.2、NVMe控制器实现 3.2 驱动软件设计方案3.2.1 读写NVMe磁盘软件驱动3.2.2 NVMe磁盘驱动设计3.2.3 标准EXT4文件系统设计 3.3 上位机控制软件设计方案 4、测试结果4.1 硬件测试平台说明4.2 测…

【鸿蒙 HarmonyOS 4.0】Web组件

一、介绍 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 二、加载网页 2.1、加载在线网页 Web组件的使用非常简单&#xff0c;只需要在Page目录下的ArkTS文件中创建一个…