3

I have trouble understand this section of the code. I know it generate a sequence of numbers, Its from https://stackoverflow.com/a/24481400/403360 But I can't make a logic out of this, I need some guide line to walk through this. especially why N-1, N-1 ?

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

and whats the effect of the make_index_sequence<sizeof...(T)>() does to the do_foo_helper? It seems it just passing to the function without a name, and something would help.

template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    std::tie(foo(std::get<I>(ts)) ...);
}

template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
    return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
Bryan Fok
  • 3,277
  • 2
  • 31
  • 59

1 Answers1

4

Let's play compiler, and substitute values for template parameters

When it encounters a type like std::make_index_sequence<3>, it goes and looks at the template and it's specialisations, and matches to

template <3>
struct make_index_sequence : public make_index_sequence<2, 2> {};

The base class of make_index_sequence<3> is another instantiation of make_index_sequence, so we repeat with new parameters

template <2, 2>
struct make_index_sequence : public make_index_sequence<1, 1, 2> {};

And again

template <1, 1, 2>
struct make_index_sequence : public make_index_sequence<0, 0, 1, 2> {};

Now we reach the specialisation

template <0, 1, 2>
struct make_index_sequence<0, 0, 1, 2> : public index_sequence<0, 1, 2> {};

Now we have a type that is inherited (indirectly) from index_sequence<0, 1, 2>, so template functions can bind a parameter pack to the 0, 1, 2 to match the arguments passed.

sizeof...(T) is just an operator to count the size of the parameter pack T, so we have things like:

template <int, bool, char>
/* ... */ do_foo(std::tuple<int, bool, char> & ts)
{
    return do_foo_helper(ts, make_index_sequence<3>());
}

Which calls

template <int, bool, char, 0, 1, 2>
/* ... */ do_foo_helper(std::tuple<int, bool, char> & ts, std::index_sequence<0, 1, 2>)
{
    std::tie(foo(std::get<0>(ts)), foo(std::get<1>(ts)), foo(std::get<2>(ts)));
}

I.e. we only need the type of the second argument, not it's value (There's only one value of type std::index_sequence<0, 1, 2> anyway, it has no data members).

Caleth
  • 52,200
  • 2
  • 44
  • 75