Reputation: 33
I see this code in gtest source file. this snippet is used to generate a template whose template variable is a sequence of integer. can anybody explain how this works?
template <size_t... Is> struct int_pack { typedef int_pack type; };
template <class Pack, size_t I>
struct append;
template <size_t... Is, size_t I>
struct append<int_pack<Is...>, I> : int_pack<Is..., I> {};
template <size_t C>
struct make_int_pack : append<typename make_int_pack<C - 1>::type, C - 1> {};
template <> struct make_int_pack<0> : int_pack<> {};
we can use make_int_pack<5>()
to generate a class instance (int_pack<0,1,2,3,4>()
) which is further used to extract 0,1,2,3,4 to do something.
Upvotes: 0
Views: 92
Reputation:
The construction is done with recursive substitution:
make_int_pack<5>
derives from append<typename make_int_pack<5-1>::type,5-1>
.
But make_int_pack<5-1>::type
is make_int_pack<4>::type
.
But make_int_pack<4>::type
is the definition from append<typename make_int_pack<4-1>::type,4-1>
, and so on.
So make_int_pack<5>
derives from
append<append<append<append<append<int_pack<>,0>,1>,2>,3>,4>
, which derives from int_pack<0,1,2,3,4>
.
To use this int_pack
we have to do something like pattern matching (c++11).
template<class T>struct test{};
template<size_t ... Is>
struct test<int_pack<Is...>>{
// use template parameter pack
};
We could then use the static members of test<make_int_pack<5>>
as result of some compiletime compilation or complex type construction.
Another possible way is to have
template<size_t... Is>
size_t test_fun(int_pack<Is...>);
,which would be called like test_fun(make_int_pack<5>());
Edit:
I'm wondering why there's two append struct, what's the difference between them?
You want to be able to write append<int_pack<0,1,2,3>,42>
. Therefor append
has to take two template parameters. That is your first declaration.
template<class T,size_t i> struct append{};
But actually you do not need the type int_pack<0,1,2,3>
but the template paramter pack used. In c++ we have partial template specialization. If the type T
happends to be an int_pack
we use the special (second) declaration. In this way we can kind of pattern match the templated type T
and get the template parameters used do define the int_pack
.
template<size_t ... Is,size_t i>
struct append<int_pack<Is...>,i> : int_pack<Is...,i>{};
what's the meaning of append,0> ?
This will fall in the second case. We have an empty template parameter pack, since the brackets are empty int_pack<>
. So we derive from int_pack<0>
and therefor our append<int_pack<>,0>::type
is int_pack<0>
.
So in summary: look at template parameter packs and partial template specialization. When working with types it is basicly just substitution of expressions.
Upvotes: 1