【问题标题】:details of std::make_index_sequence and std::index_sequencestd::make_index_sequence 和 std::index_sequence 的详细信息
【发布时间】:2018-09-15 03:13:14
【问题描述】:

我很享受增加可变参数模板的乐趣,并开始摆弄这个新功能。我试图了解std::index_sequence 的实现细节(用于元组实现)。我在那里看到了示例代码,但我真的想要一步一步地解释std::index_sequence 的编码方式以及每个阶段的元编程主体。想想真的傻了:)

【问题讨论】:

    标签: c++ templates c++14 variadic-templates template-meta-programming


    【解决方案1】:

    我在那里看到了示例代码,但我真的想要一步一步地解释如何对 index_sequence 进行编码以及每个阶段的元编程原则。

    你问的问题并不容易解释......

    嗯...std::index_sequence本身很简单:定义如下

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

    实质上,它是无符号整数的模板容器。

    棘手的部分是std::make_index_sequence 的实现。也就是说:棘手的部分是从std::make_index_sequence&lt;N&gt; 传递到std::index_sequence&lt;0, 1, 2, ..., N-1&gt;

    我建议你一个可能的实现(不是一个很好的实现,但简单(我希望)理解),我会尝试解释它是如何工作的。

    不完全是从std::integer_sequence 传递的标准索引序列,但修复std::size_t 类型,您可以获得合理的indexSequence/makeIndexSequence 对,并使用以下代码。

    // index sequence only
    template <std::size_t ...>
    struct indexSequence
     { };
    
    template <std::size_t N, std::size_t ... Next>
    struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
     { };
    
    template <std::size_t ... Next>
    struct indexSequenceHelper<0U, Next ... >
     { using type = indexSequence<Next ... >; };
    
    template <std::size_t N>
    using makeIndexSequence = typename indexSequenceHelper<N>::type;
    

    我认为了解其工作原理的一个好方法是遵循一个实际示例。

    我们可以点对点地看到makeIndexSequence&lt;3&gt;如何变成index_sequenxe&lt;0, 1, 2&gt;

    • 我们将makeIndexSequence&lt;3&gt; 定义为typename indexSequenceHelper&lt;3&gt;::type [N3]

    • indexSequenceHelper&lt;3&gt; 仅匹配一般情况,因此从indexSequenceHelper&lt;2, 2&gt; 继承[N3Next... 为空]

    • indexSequenceHelper&lt;2, 2&gt; 仅匹配一般情况,因此从indexSequenceHelper&lt;1, 1, 2&gt; 继承 [N is 2 and Next... is 2]

    • indexSequenceHelper&lt;1, 1, 2&gt; 仅匹配一般情况,因此从 indexSequenceHelper&lt;0, 0, 1, 2&gt; 继承 [N is 1 and Next... is 1, 2]

    • indexSequenceHelper&lt;0, 0, 1, 2&gt; 匹配两种情况(一般为偏特化),因此应用偏特化并定义 type = indexSequence&lt;0, 1, 2&gt; [Next...0, 1, 2]

    结论:makeIndexSequence&lt;3&gt;indexSequence&lt;0, 1, 2&gt;

    希望这会有所帮助。

    --- 编辑---

    一些说明:

    • std::index_sequencestd::make_index_sequence 从 C++14 开始可用

    • 我的示例很简单(我希望)可以理解,但是(正如 aschepler 所指出的)有很大的限制,即线性实现;我的意思是:如果你需要index_sequence&lt;0, 1, ... 999&gt;,使用makeIndexSequence&lt;1000&gt;,你以递归方式实现1000个不同的indexSequenceHelper;但是有一个递归限制(编译器形式编译器不同)可以小于 1000;还有其他算法会限制递归次数,但解释起来更复杂。

    【讨论】:

    • 一个很好的答案,但我要补充的是,您可能会发现一些实现要复杂得多:它们试图减少模板实例化的数量和/或深度,因为可能存在编译器限制递归实例化的深度,减少总数可以加快编译时间。
    • 谢谢!那太棒了。正是我所追求的。
    • @aschepler - 我知道,我知道......我通常(在 C++11 中)使用具有对数复杂度的不同实现。但我的意图是让它简单易懂。也许我会尝试更好地解释这一点......
    • @max66 是的,这个简单的解释肯定更适合这个问题。只是建议一些额外的信息。
    • @user3613174 - 添加了一些说明(另请参阅 aschepler 的评论)。
    【解决方案2】:

    为了完整起见,我将添加一个更现代的std::make_index_sequence 实现,使用if constexprauto,使模板编程更像“普通”编程。

    template <std::size_t... Ns>
    struct index_sequence {};
    
    template <std::size_t N, std::size_t... Is>
    auto make_index_sequence_impl() {
        // only one branch is considered. The other may be ill-formed
        if constexpr (N == 0) return index_sequence<Is...>(); // end case
        else return make_index_sequence_impl<N-1, N-1, Is...>(); // recursion
    }
    
    template <std::size_t N>
    using make_index_sequence = std::decay_t<decltype(make_index_sequence_impl<N>())>;
    

    我强烈建议使用这种更容易推理的模板编程风格。

    【讨论】:

    • 有趣...是的,if constexpr 变得更简单。但您应该明确指出,if constexpr 从 C++17 开始可用。
    【解决方案3】:

    别忘了:

    template <std::size_t N, std::size_t ...I>
    constexpr auto make_index_sequence_impl() noexcept
    {
      if constexpr (!N)
      {
        return std::index_sequence<I...>();
      }
      else if constexpr (!sizeof...(I))
      {
        return make_index_sequence_impl<N - 1, 0>();
      }
      else if constexpr (N >= sizeof...(I))
      {
        return make_index_sequence_impl<N - sizeof...(I), I..., sizeof...(I) + I...>();
      }
      else
      {
        return []<auto ...J>(std::index_sequence<J...>) noexcept
        {
          return std::index_sequence<I..., sizeof...(I) + J...>();
        }(make_index_sequence_impl<N>()); // index concatenation
      }
    }
    
    template <size_t N>
    using make_index_sequence = decltype(make_index_sequence_impl<N>());
    

    【讨论】:

      猜你喜欢
      • 2019-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多