是否有可能“存储”一个模板参数包而不展开它?

当我偶然发现这个问题时,我正在试验 C + + 0x 可变模板:

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在尝试输入模板参数包时给出了一个错误。

基本上,我希望将参数包“存储”在 typedef 中,而不解压缩它。有可能吗?如果没有,是否有什么原因不允许这样做?

23446 次浏览

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;

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()
{}

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() {}

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.