std :: make_index_sequence和std :: index_sequence的详细信息

时间:2018-04-05 10:21:55

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

我很喜欢使用可变参数模板,并开始摆弄这个新功能。我试图了解std::index_sequence的实现细节(用于元组实现)。我看到那里的示例代码,但我真的想要一步一步地解释std::index_sequence如何被编码以及每个阶段的元编程主题。认为真的愚蠢了:)

3 个答案:

答案 0 :(得分:12)

  

我看到那里的示例代码,但我真的想要逐步解释如何编码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<N>传递到std::index_sequence<0, 1, 2, ..., N-1>

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

完全不是从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<3>成为index_sequenxe<0, 1, 2>的方式。

  • 我们认为makeIndexSequence<3>定义为typename indexSequenceHelper<3>::type [N3]

  • indexSequenceHelper<3>仅匹配一般情况,因此继承自indexSequenceHelper<2, 2> [N3Next...为空]

    < / LI>
  • indexSequenceHelper<2, 2>仅匹配一般情况,因此继承自indexSequenceHelper<1, 1, 2> [N2Next...2] < / p>

  • indexSequenceHelper<1, 1, 2>仅匹配一般情况,因此继承自indexSequenceHelper<0, 0, 1, 2> [N1Next...1, 2] < / p>

  • indexSequenceHelper<0, 0, 1, 2>匹配两种情况(一般是部分特化),因此应用了部分特化,定义type = indexSequence<0, 1, 2> [Next...0, 1, 2]

结论:makeIndexSequence<3>indexSequence<0, 1, 2>

希望这有帮助。

---编辑---

一些澄清:

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

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

答案 1 :(得分:5)

为了完整起见,我将使用std::make_index_sequenceif constexpr添加更为现代的auto实现,使模板编程更像&#34 ;正常&#34;节目。

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>())>;

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

答案 2 :(得分:0)

以免被遗忘:

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>());