Is it possible to "store" a template parameter pack without expanding it?
Another approach, which is slightly more generic than Ben's, is as follows:
#include <tuple>
template <typename... Args>
struct variadic_typedef
{
// this single type represents a collection of types,
// as the template arguments it took to define it
};
template <typename... Args>
struct convert_in_tuple
{
// base case, nothing special,
// just use the arguments directly
// however they need to be used
typedef std::tuple<Args...> type;
};
template <typename... Args>
struct convert_in_tuple<variadic_typedef<Args...>>
{
// expand the variadic_typedef back into
// its arguments, via specialization
// (doesn't rely on functionality to be provided
// by the variadic_typedef struct itself, generic)
typedef typename convert_in_tuple<Args...>::type type;
};
typedef variadic_typedef<int, float> myTypes;
typedef convert_in_tuple<myTypes>::type int_float_tuple;
int main()
{}
I think the reason it's not allowed is that it would be messy, and you can work around it. You need to use dependency inversion and make the struct storing the parameter pack into a factory template able to apply that parameter pack to another template.
Something along the lines of:
template < typename ...Args >
struct identities
{
template < template<typename ...> class T >
struct apply
{
typedef T<Args...> type;
};
};
template < template<template<typename ...> class> class T >
struct convert_in_tuple
{
typedef typename T<std::tuple>::type type;
};
typedef convert_in_tuple< identities< int, float >::apply >::type int_float_tuple;
I've found Ben Voigt's idea very useful in my own endeavors. I've modified it slightly to make it general to not just tuples. To the readers here it might be an obvious modification, but it may be worth showing:
template <template <class ... Args> class T, class ... Args>
struct TypeWithList
{
typedef T<Args...> type;
};
template <template <class ... Args> class T, class ... Args>
struct TypeWithList<T, VariadicTypedef<Args...>>
{
typedef typename TypeWithList<T, Args...>::type type;
};
The name TypeWithList stems from the fact that the type is now instantiated with a previous list.