Is it possible to "store" a template parameter pack without expanding it?
Solution 1
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()
{}
Solution 2
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;
Solution 3
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.
Solution 4
This is a variation of GManNickG's neat partial specialization trick. No delegation, and you get more type safety by requiring the use of your variadic_typedef struct.
#include <tuple>
template<typename... Args>
struct variadic_typedef {};
template<typename... Args>
struct convert_in_tuple {
//Leaving this empty will cause the compiler
//to complain if you try to access a "type" member.
//You may also be able to do something like:
//static_assert(std::is_same<>::value, "blah")
//if you know something about the types.
};
template<typename... Args>
struct convert_in_tuple< variadic_typedef<Args...> > {
//use Args normally
typedef std::tuple<Args...> type;
};
typedef variadic_typedef<int, float> myTypes;
typedef convert_in_tuple<myTypes>::type int_float_tuple; //compiles
//typedef convert_in_tuple<int, float>::type int_float_tuple; //doesn't compile
int main() {}
Geekoder
I am a doctor in software engineering, working as a senior software developer for Allegorithmic.
Updated on July 05, 2022Comments
-
Geekoder almost 2 years
I was experimenting with C++0x variadic templates when I stumbled upon this issue:
template < typename ...Args > struct identities { typedef Args type; //compile error: "parameter packs not expanded with '...' }; //The following code just shows an example of potential use, but has no relation //with what I am actually trying to achieve. template < typename T > struct convert_in_tuple { typedef std::tuple< typename T::type... > type; }; typedef convert_in_tuple< identities< int, float > >::type int_float_tuple;
GCC 4.5.0 gives me an error when I try to typedef the template parameters pack.
Basically, I would like to "store" the parameters pack in a typedef, without unpacking it. Is it possible? If not, is there some reason why this is not allowed?
-
Geekoder over 13 yearsI tried your code on GCC 4.5, you just need to change
typename T
inclass T
and change theconvert_in_tuple
parameter to be a template template template parameter:template < template< template < typename ... > class > class T > struct convert_in_tuple {...}
(!). -
Ben Voigt over 13 years@Luc: Edited to be a template template template parameter. Replacing
typename
withclass
feels a little dubious, since the draft says "There is no semantic difference betweenclass
andtemplate
in a template-parameter.", could you try this new code? -
Geekoder over 13 yearsI can't find it in the standard, but I think I remember that for template template parameters you need to use
class
and nottypename
(because a template type is inevitably a class and not any type). -
Ben Voigt over 13 years@Luc: Got it to compile in gcc 4.5.2 in a VM, thanks for the pointers. Now struggling to get copy+paste out of the VM to work...
-
Geekoder over 13 yearsIndeed, the standard says in §14.1.2 that there is no difference between
class
andtypename
, but just above (in §14.1.1), the syntax only allows theclass
keyword in template template parameter declaration. Even though this can seem inconsistent, I think the rationale is, like I said before, that a template template parameter can't be any type (e.g. it can't beint
orbool
), so perhaps the committee decided that the use oftypename
would be misleading. Anyway, let's get back to the subject :)! -
Geekoder over 13 yearsYour solution is nice, but it sad that we need to employ such workarounds...However, it is true that supporting this in a clean way could have been a bit hard, it would have meant that a typedef could be either a plain type or a parameter pack, which is weird...Perhaps a new syntax could have been used, e.g. a 'packed typedef' (
typedef ...Args args
or something along this way). -
Geekoder over 13 yearsVery good workaround, I did not think about using partial template specialization!
-
Jason over 12 years@GMan: quick question ... this was helpful, but should the partially specialized version actually be
typedef typename convert_in_tuple<Args...>::type type;
, or does that not matter? -
GManNickG over 12 years@Jason: That's correct. I'm surprised my answer's gotten by so long without a keen eye noticing. :)
-
Casey Rodarmor about 12 yearsBaller solution! I love the madness that is C++.
-
Geekoder about 11 yearsThere was no recursion in @GManNickG's answer, and I think the ability of using a raw parameter pack instead of
variadic_typedef
was meant to be a feature. Hence, I would say this answer is more a degradation than a refinement... -
jack about 11 yearsI understand that the option of not using a variadic_typedef was intended to be a feature, but one man's feature is another man's bug. The reason I was on this thread in the first place was to find a way to do exactly what my answer does here. Also, there is a single recursive "call" in @GManNickG's solution - when the variadic_typdef partial specialization of convert_in_tuple "delegates" to the unspecialized version. Without it, things are slightly simpler. And lastly, I chose the word refinement not to cast my solution as better, but as more specific. I changed my wording to reflect this.
-
Yakk - Adam Nevraumont over 10 yearsI'd be a bit concerned: while it is very tempting to say "treat a list of types, or an instance of a particular type that contains a list, as the same thing", in my experience things tend to explode messily when you do that. As an example, imagine a list of length 1 containing a
variadic_typedef
and how it interacts with the above code. Now imagine a list of types that are each passed into aconvert_in_tuple
and how it interacts with the above code. If you start setting up a few levels of indirection, treating the container and the contents as interchangable causes problems. -
Yakk - Adam Nevraumont over 10 yearsYou can remove dependency on
variadic_typedef
forconvert_in_tuple
-- have it taketemplate<typename Pack> struct convert_in_tuple {};
, then specializetemplate<template<typename...>class Pack, typename...Args> struct convert_in_tuple<Pack<Args...>> { typedef std::tuple<Args> type; }
-- now any variardic pack can be mapped to atuple
. -
JPHarford over 9 yearsI don't understand how this solves OP's problem. The convert_in_tuple struct contains an alias of an alias of a tuple. The type that it represents is a tuple with the Args ... parameter pack, and not the Args ... parameter pack itself.
-
Ben Voigt about 4 years@user2813810: The thing representing the parameter pack itself is
variadic_typedef
,convert_in_tuple
is just one example of how to get the parameter pack back out in a usable fashion.