73

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?

Luc Touraille
  • 72,907
  • 15
  • 82
  • 134

4 Answers4

57

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()
{}
GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • 1
    Very good workaround, I did not think about using partial template specialization! – Luc Touraille Jan 15 '11 at 20:13
  • @GMan: quick question ... this was helpful, but should the partially specialized version actually be `typedef typename convert_in_tuple::type type;`, or does that not matter? – Jason Aug 20 '11 at 15:10
  • @Jason: That's correct. I'm surprised my answer's gotten by so long without a keen eye noticing. :) – GManNickG Aug 20 '11 at 15:31
  • Baller solution! I love the madness that is C++. – Casey Rodarmor Feb 26 '12 at 00:07
  • 3
    I'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 a `convert_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 Aug 12 '13 at 15:33
  • 1
    I 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. – user2813810 Jan 28 '15 at 19:15
  • @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. – Ben Voigt Apr 27 '20 at 01:33
10

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;
Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
  • I tried your code on GCC 4.5, you just need to change `typename T` in `class T` and change the `convert_in_tuple` parameter to be a template template template parameter: `template < template< template < typename ... > class > class T > struct convert_in_tuple {...}` (!). – Luc Touraille Jan 14 '11 at 14:51
  • 1
    @Luc: Edited to be a template template template parameter. Replacing `typename` with `class` feels a little dubious, since the draft says "There is no semantic difference between **`class`** and **`template`** in a *template-parameter*.", could you try this new code? – Ben Voigt Jan 14 '11 at 14:53
  • I can't find it in the standard, but I think I remember that for template template parameters you need to use `class` and not `typename` (because a template type is inevitably a class and not any type). – Luc Touraille Jan 14 '11 at 15:01
  • @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... – Ben Voigt Jan 14 '11 at 15:10
  • Indeed, the standard says in §14.1.2 that there is no difference between `class` and `typename`, but just above (in §14.1.1), the syntax only allows the `class` 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 be `int` or `bool`), so perhaps the committee decided that the use of `typename` would be misleading. Anyway, let's get back to the subject :)! – Luc Touraille Jan 14 '11 at 15:35
  • Your 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). – Luc Touraille Jan 14 '11 at 15:59
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.

Werner Erasmus
  • 3,702
  • 14
  • 27
2

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() {}
jack
  • 1,824
  • 1
  • 16
  • 17
  • 1
    There 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... – Luc Touraille Feb 18 '13 at 08:26
  • I 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. – jack Mar 05 '13 at 17:26
  • You can remove dependency on `variadic_typedef` for `convert_in_tuple` -- have it take `template struct convert_in_tuple {};`, then specialize `template – Yakk - Adam Nevraumont Aug 12 '13 at 15:36