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

use*_*174 15 c++ templates template-meta-programming variadic-templates c++14

我很喜欢增加可变参数模板,并开始摆弄这个新功能.我试图了解std::index_sequences 的实现细节(用于元组实现).我看到那里的示例代码,但我真的想要逐步解释如何std::index_sequence编码和每个阶段的元编程主题.想真的傻了:)

max*_*x66 21

我看到那里的示例代码,但我真的想要逐步解释index_sequence如何编码以及每个阶段的元编程主题.

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

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

template<std::size_t... Ints>
using index_sequence = std::integer_sequence<std::size_t, Ints...>;
Run Code Online (Sandbox Code Playgroud)

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

棘手的部分是实施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;
Run Code Online (Sandbox Code Playgroud)

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

我们可以看到,点到点,如何makeIndexSequence<3>成为index_sequenxe<0, 1, 2>.

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

  • indexSequenceHelper<3>只匹配一般情况,所以从indexSequenceHelper<2, 2>[ Nis 3and Next...is empty] 继承

  • indexSequenceHelper<2, 2>只匹配一般情况所以继承自indexSequenceHelper<1, 1, 2>[ N2Next...2]

  • indexSequenceHelper<1, 1, 2>只匹配一般情况所以继承自indexSequenceHelper<0, 0, 1, 2>[ N1Next...1, 2]

  • 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; 还有其他算法可以限制递归次数,但解释起来却更复杂.

  • 一个很好的答案,但我想补充一点,您可能会发现某些实现相当复杂,这是有原因的:它们试图减少模板实例化的数量和/或深度,因为编译器对递归实例化的深度可能存在限制,并且减少总数可以使编译时间更快。 (2认同)
  • @aschepler - 我知道,我知道...我通常(在 C++11 中)使用具有对数复杂度的不同实现。但我的目的是让它变得简单易懂。也许我会尝试更好地解释这一点...... (2认同)

pap*_*aga 10

为了完整起见,我将添加更现代的实现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>())>;
Run Code Online (Sandbox Code Playgroud)

我强烈建议使用这种样式的模板编程,这很容易推理。

  • 有趣的...是的,通过`if constexpr`变得更简单。但是您应该精确说明`if constexpr`从C ++ 17开始可用。 (9认同)