2011-01-14 10 views
68

ich mit C++ 0x variadische Vorlagen zu experimentieren, wenn ich auf dieses Problem gestoßen:Kann ein Template-Parameterpaket "gespeichert" werden, ohne es zu erweitern?

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 gibt mir einen Fehler, wenn ich versuche, den Template-Parameter Pack typedef.

Grundsätzlich möchte ich das Parameter-Paket in einem Typedef "speichern", ohne es zu entpacken. Ist es möglich? Wenn nicht, gibt es einen Grund, warum das nicht erlaubt ist?

Antwort

50

Ein weiterer Ansatz, der etwas allgemeineren als Bens ist, ist wie folgt:

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

Schön, sehr ähnlich zu dem, was das OP ursprünglich versuchte. –

+1

Sehr gute Problemumgehung, ich habe nicht darüber nachgedacht, eine teilweise Template-Spezialisierung zu verwenden! –

+0

@GMan: schnelle Frage ...Das war hilfreich, aber sollte die teilweise spezialisierte Version tatsächlich 'typedef typename convert_in_tuple :: type type' sein, oder ist das egal? – Jason

8

Ich denke, der Grund, warum es nicht erlaubt ist, ist, dass es chaotisch wäre, und Sie können es umgehen. Sie müssen die Abhängigkeitsinversion verwenden und die Struktur, die das Parameterpaket speichert, zu einer Factory-Vorlage machen, die dieses Parameterpaket auf eine andere Vorlage anwenden kann.

Etwas entlang der Linien von:

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; 
+0

Ich habe Ihren Code auf GCC 4.5 versucht, Sie müssen nur 'typename T' in' Klasse T' ändern und ändern Sie den 'convert_in_tuple' Parameter zu einem Vorlagen-Vorlagenparameter:' Vorlage Klasse> Klasse T> struct convert_in_tuple {...} '(!). –

+1

@Luc: Bearbeitet als Template Template Template Parameter. Das Ersetzen von "typename" durch "class" fühlt sich ein wenig zweifelhaft an, da der Entwurf sagt: "Es gibt keinen semantischen Unterschied zwischen **' class' ** und ** 'template' ** in einem * template-Parameter *.", Könnte Sie versuchen diesen neuen Code? –

+0

Ich kann es nicht im Standard finden, aber ich denke, ich erinnere mich, dass für Template-Template-Parameter müssen Sie 'class' und nicht' typename' (weil ein Template-Typ ist unvermeidlich eine Klasse und nicht jeder Typ). –

2

Dies ist eine Variation von GManNickG des ordentlichen Teil Spezialisierung Trick. Keine Delegierung, und Sie erhalten mehr Typsicherheit, indem Sie die Verwendung Ihrer variadic_typedef-Struktur verlangen.

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

Es gab keine Rekursion in der @ GManNickG Antwort, und ich denke, dass die Fähigkeit, ein rohe Parameter Pack anstelle von 'variadic_typedef' zu verwenden, ein Feature sein sollte. Daher würde ich sagen, dass diese Antwort mehr eine Verschlechterung als eine Verfeinerung ist ... –

+0

Ich verstehe, dass die Option, ein Variadic_Typedef nicht zu verwenden, ein Feature sein sollte, aber ein Mann ist ein anderer Mann Fehler. Der Grund, warum ich in diesem Thread war, war, einen Weg zu finden, genau das zu tun, was meine Antwort hier tut. Außerdem gibt es einen einzigen rekursiven "Aufruf" in der @ GManNickG-Lösung - wenn die variadic_typef-Teilspezialisierung von convert_in_tuple auf die unspezialisierte Version "delegiert". Ohne es sind die Dinge etwas einfacher. Und schließlich wählte ich das Wort Verfeinerung, um meine Lösung nicht besser, sondern spezifischer zu formulieren. Ich habe meine Formulierung geändert, um dies zu berücksichtigen. – jack

+0

Sie können die Abhängigkeit von 'variadic_typedef' für' convert_in_tuple' entfernen - nehmen Sie 'template struct convert_in_tuple {};', dann spezialisieren Sie