Ist es möglich, ein Vorlagenparameterpaket zu „speichern“, ohne es zu erweitern?

72

Ich habe mit variablen C ++ 0x-Vorlagen experimentiert, als ich auf dieses Problem gestoßen bin:

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 eine Fehlermeldung aus, wenn ich versuche, das Vorlagenparameterpaket einzugeben.

Grundsätzlich möchte ich das Parameterpaket in einem typedef "speichern", ohne es zu entpacken. Ist es möglich? Wenn nicht, gibt es einen Grund, warum dies nicht zulässig ist?

Luc Touraille
quelle

Antworten:

56

Ein anderer Ansatz, der etwas allgemeiner ist als der von Ben, lautet 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()
{}
GManNickG
quelle
1
Sehr gute Problemumgehung, ich habe nicht über eine teilweise Template-Spezialisierung nachgedacht!
Luc Touraille
@GMan: kurze Frage ... das war hilfreich, aber sollte die teilweise spezialisierte Version tatsächlich sein typedef typename convert_in_tuple<Args...>::type type;, oder spielt das keine Rolle?
Jason
@ Jason: Das stimmt. Ich bin überrascht, dass meine Antwort so lange vorbei ist, ohne dass ein scharfes Auge es bemerkt. :)
GManNickG
3
Ich wäre ein bisschen besorgt: Während es sehr verlockend ist zu sagen, "eine Liste von Typen oder eine Instanz eines bestimmten Typs, der eine Liste enthält, als dasselbe zu behandeln", neigen die Dinge meiner Erfahrung nach dazu, unordentlich zu explodieren, wenn Sie TU das. Stellen Sie sich als Beispiel eine Liste der Länge 1 vor, die a enthält variadic_typedefund wie sie mit dem obigen Code interagiert. Stellen Sie sich nun eine Liste von Typen vor, die jeweils an a übergeben werden, convert_in_tupleund wie sie mit dem obigen Code interagieren. Wenn Sie einige Indirektionsebenen einrichten, führt die Behandlung des Containers und des Inhalts als austauschbar zu Problemen.
Yakk - Adam Nevraumont
1
Ich verstehe nicht, wie dies das Problem von OP löst. Die Struktur convert_in_tuple enthält einen Alias ​​eines Alias ​​eines Tupels. Der Typ, den es darstellt, ist ein Tupel mit dem Parameterpaket Args ... und nicht mit dem Parameterpaket Args ... selbst.
user2813810
10

Ich denke, der Grund, warum es nicht erlaubt ist, ist, dass es chaotisch wäre und man es umgehen kann. Sie müssen die Abhängigkeitsinversion verwenden und die Struktur, die das Parameterpaket in einer Factory-Vorlage speichert, in die Lage versetzen, dieses Parameterpaket auf eine andere Vorlage anzuwenden.

Etwas in der Art 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;
Ben Voigt
quelle
Ich habe versucht , den Code auf GCC 4.5, müssen Sie nur ändern typename Tin class Tund ändern convert_in_tupleParameter einen Template - Template - Template - Parameter zu sein: template < template< template < typename ... > class > class T > struct convert_in_tuple {...}(!).
Luc Touraille
1
@Luc: Bearbeitet, um ein Vorlagenvorlagenparameter zu sein. Das Ersetzen typenamedurch classfühlt sich etwas zweifelhaft an, da im Entwurf steht "Es gibt keinen semantischen Unterschied zwischen classund templatein einem Vorlagenparameter .". Könnten Sie diesen neuen Code ausprobieren?
Ben Voigt
Ich kann es nicht im Standard finden, aber ich denke, ich erinnere mich, dass Sie für Vorlagenvorlagenparameter verwenden müssen classund nicht typename(weil ein Vorlagentyp zwangsläufig eine Klasse und kein Typ ist).
Luc Touraille
@ Luc: Ich habe es in gcc 4.5.2 in einer VM kompiliert, danke für die Zeiger. Jetzt kämpfen, um Kopieren + Einfügen aus der VM zum Laufen zu bringen ...
Ben Voigt
In der Tat besagt der Standard in §14.1.2, dass es keinen Unterschied zwischen classund gibt typename, aber genau darüber (in §14.1.1) erlaubt die Syntax nur das classSchlüsselwort in der Parameterdeklaration der Vorlagenvorlage. Auch wenn dies inkonsistent erscheinen kann, denke ich, dass die Begründung, wie ich bereits sagte, darin besteht, dass ein Vorlagenvorlagenparameter kein Typ sein kann (z. B. nicht intoder nicht bool), weshalb das Komitee möglicherweise entschieden hat, dass die Verwendung von typenamesein soll irreführend. Wie auch immer, kommen wir zurück zum Thema :)!
Luc Touraille
3

Ich fand Ben Voigts Idee sehr nützlich für meine eigenen Bemühungen. Ich habe es leicht modifiziert, um es allgemein zu machen und nicht nur Tupel. Für die Leser hier mag es eine offensichtliche Modifikation sein, aber es kann sich lohnen, Folgendes zu zeigen:

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;
};

Der Name TypeWithList ergibt sich aus der Tatsache, dass der Typ jetzt mit einer vorherigen Liste instanziiert wird.

Werner Erasmus
quelle
2

Dies ist eine Variation von GManNickGs ordentlichem Teilspezialisierungstrick. Keine Delegierung, und Sie erhalten mehr Typensicherheit, wenn Sie die Verwendung Ihrer variadic_typedef-Struktur benötigen.

#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
quelle
1
In der Antwort von @ GManNickG gab es keine Rekursion, und ich denke, dass die Möglichkeit, ein Rohparameterpaket anstelle von variadic_typedefzu verwenden, eine Funktion sein sollte. Daher würde ich sagen, dass diese Antwort eher eine Verschlechterung als eine Verfeinerung ist ...
Luc Touraille
Ich verstehe, dass die Option, kein variadic_typedef zu verwenden, eine Funktion sein sollte, aber die Funktion eines Mannes ist der Fehler eines anderen Mannes. Der Grund, warum ich überhaupt in diesem Thread war, war, einen Weg zu finden, genau das zu tun, was meine Antwort hier tut. Außerdem gibt es in der Lösung von @ GManNickG einen einzigen rekursiven "Aufruf" - wenn die partielle Spezialisierung variadic_typdef von convert_in_tuple die nicht spezialisierte Version "delegiert". Ohne sie sind die Dinge etwas einfacher. Und schließlich habe ich das Wort Verfeinerung gewählt, um meine Lösung nicht als besser, sondern als spezifischer zu betrachten. Ich habe meinen Wortlaut geändert, um dies widerzuspiegeln.
Jack
Sie können die Abhängigkeit von variadic_typedeffor entfernen convert_in_tuple- lassen Sie es sich nehmen template<typename Pack> struct convert_in_tuple {};und dann spezialisieren template<template<typename...>class Pack, typename...Args> struct convert_in_tuple<Pack<Args...>> { typedef std::tuple<Args> type; }- jetzt kann jedes Variardic Pack einem zugeordnet werden tuple.
Yakk - Adam Nevraumont