• C++模板 - index_sequence


    integer_sequence 是 c++ 14中新增加的一个元编程工具

    其衍生出来的还有如 index_sequencemake_index_sequenceindex_sequence_for等辅助工具

    现在,让我们来浅尝一下这些东西吧!

    integer_sequence

    integer_sequence 其实没有什么特殊的,就是一个类

    只不过他是index_sequence的基础

    template<typename _Tp, _Tp... _Idx>
      struct integer_sequence
      {
        typedef _Tp value_type;
        static constexpr size_t size() noexcept { return sizeof...(_Idx); }
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    index_sequence

    index_sequence 会在编译期生成一个从0开始的序列

    然后你就可以对其进行一些奇奇怪怪的操作

    template <size_t... N> void print(std::index_sequence<N...>) {
      std::vector<int> res;
      (void)std::initializer_list<int>{
          ((res.push_back(N), std::cout << N << " "), 0)...};
      std::for_each(res.begin(), res.end(), [](int x) {std::cout << x << " ";});
    }
    int main() {
      auto t = std::make_index_sequence<10>();
      print(t);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    比如 ((res.push_back(N), std::cout << N << " "), 0)...这句话,就会在编译期被展开

    这里展开在了一个初始化列表中,小技巧

    make_index_sequence

    那么,index_sequence 是如何生成的呢?

    有两种形式

    • 编译器内建
    • 递归式的生成

    第二种方式嘛还是用到了元编程的惯用伎俩,特化,递归式的编程

    template <int... N> struct index_seq {};
    template <int N, int... M>
    struct make_index_seq : public make_index_seq<N - 1, N - 1, M...> {};
    template <int... M> struct make_index_seq<0, M...> : public index_seq<M...> {};
    
    • 1
    • 2
    • 3
    • 4

    对齐使用也是一样的形式

    template <int... N> void print(index_seq<N...>) {
      (void)std::initializer_list<int>{((std::cout << N << " "), 0)...};
    }
    
    int main() {
      auto r = make_index_seq<100>();
      print(r);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    使用场景

    刚才,看见了print去打印的时候打印了 0-(N-1)的元素

    那么,这个行为是在编译期展开的,我们就可以用到其他需要常量的地方

    比如一个简单的需求:

    打印tuple

    template <typename T, typename F, int...N>
    void exec_for_tuple(const T& tup, F&& func, index_seq<N...>) {
      (void)std::initializer_list<int> {
          (func(std::get<N>(tup)), 0)...
      };
    }
    
    template <typename Func, typename ...Arg>
    void for_tuple(Func&& func, std::tuple<Arg...> tuple) {
      exec_for_tuple(tuple, std::forward<Func>(func), make_index_seq<sizeof...(Arg)>());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    exec_for_tuple部分应该非常好懂,但是为什么中间还要再转发一层呢?

    因为tuple元素的个数我们不能直接获取到,我们写的又要是一个通用的函数

    所以要通过 sizeof...(arg) 这种伎俩来将其元素个数计算出来

    如何调用呢?

    如下所示:

    std::tuple<int, int, double> tuple{1, 2, 3.0};
    for_tuple([](auto t) {
      std::cout << t << " ";
    }, tuple);
    
    • 1
    • 2
    • 3
    • 4

    index_sequence_for

    那么,看到现在,你知道 index_sequence_for又是何物吗?

    其实吧,刚才就已经见过 index_sequence_for这个东西了

    其实就是计算可变长模板参数的个数,然后将其长度做成一个sequence出来

    template<typename... _Types>
    using index_sequence_for = make_index_seq<sizeof...(_Types)>;
    
    • 1
    • 2

    结语

    index_sequence 是一个不起眼的家伙,可能你平时都不会去了解它,但是这个东西的用途还是有很多的,你想想,编译器制造一串序列的能力,在tuple这种模板中,使用其是不是更加方便了,在bind这种模板中的参数上,若是使用它,是不是更加灵活好些了。

    其实和tuple一个道理,在平常编程中,你也许不会使用它,但是在模板编程中,这是比较好的一个工具。

    {amjieker}

  • 相关阅读:
    Spring整合Junit(单元测试)
    Spring MVC注解Controller源码流程解析--定位HandlerMethod
    ATtiny88初体验(七):TWI
    pg数据表同步到hive表数据压缩总结
    28栈与队列-单调队列
    【测试人生】GAutomator安卓UE4版本的实现机理与优化实战
    计算机网络(二)
    nacos2-配置中心
    秋招挂麻了,就差去送外卖了,10w字Java八股啃完,春招必拿下
    详解4种类型的爬虫技术
  • 原文地址:https://blog.csdn.net/qq_51986723/article/details/127602490