2010-09-22 7 views
1

J'essaie de créer un modèle de fonction qui acceptera deux (ou plus) des modèles de classe variée imbriqués listés ci-dessous, en tant qu'arguments, et les placer dans une autre structure de données acceptera différents types (la paire ou le tuple est ce que j'utiliserai probablement). Voici les classes et sous-classes, ainsi que l'utilisation de ma fonction (la fonction est définie plus bas):Accepter des modèles de classe variadique imbriqués comme arguments du modèle de fonction

template<typename... Args> struct Entity { 

    template<typename... InnerEntArgs> struct InnerEntity { 
     InnerEntity(InnerEntArgs... inner_ent_args) { 
      ... //do stuff w/ InnerEntArgs pack 
      ... //do stuff that makes Inner dependent on Outer's Args pack 
     } 
    }; 
}; 

struct ThingA : Entity<int, string> { 
    ... //construct ThingA 
}; 

struct ThingB : Entity<string, string> { 
    ... //construct ThingB 
}; 

auto foo = my_func(
    ThingA::InnerEntity<int, int, int>(1, 2, 3) 
    , ThingB::InnerEntity<string, int>("bar", 1) 
); 

Voici le code que je bricolé pour la fonction, et il ne compile très bien, mais je Je ne sais pas si la configuration est correcte. Plus précisément, je suis un peu floue sur la façon dont typename et ::template sont fait le compilateur heureux dans ce contexte, ou si cette fonction se comportera de la façon dont je me attends:

template< 
    typename... ArgsA, typename... ArgsAInner 
    , typename... ArgsB, typename... ArgsBInner 
> auto my_func(
    typename Entity<ArgsA...>::template InnerEntity<ArgsAInner...> A 
    , typename Entity<ArgsB...>::template InnerEntity<ArgsBInner...> B 
) -> tuple<decltype(A), decltype(B)> { 
    return make_tuple(A, B); 
} 

Je pense J'ai un bon saisissez comment les packs de paramètres sont déduits/déduits, et comment auto, decltype, et le type de retour arrière font leur chose, mais si je me trompe, s'il vous plaît laissez-moi savoir comment. De plus, si quelqu'un veut démontrer une version variée de cette fonction qui peut accepter n'importe quel nombre de modèles de classes variées imbriqués et les placer dans un conteneur ou une structure de données appropriés, ce serait génial, mais je suis principalement préoccupé de comprendre pleinement typename et ::template. Merci d'avance!

* Si j'ai mal écrit ce titre ou si je mélange des termes, veuillez expliquer. :) Je suis ici pour apprendre.

Répondre

4

Cela ne fonctionnera pas car Entity<Args>::InnerEntity est un contexte non déductible. Signifie que ArgsA... et ArgsAInner... ne peuvent pas être déduits, de même pour l'autre paramètre. C'est parce que avant que le compilateur puisse en déduire Args, il doit savoir de quel type InnerEntity est membre, mais pour savoir que, il doit déduire Args.

Vous pouvez mettre cette fonction en tant que modèle de fonction ami dans Entity<Args...> et la faire fonctionner tant que les deux sont membres du même modèle. Mais la dernière fois que j'ai vérifié, GCC n'a pas trouvé de fonctions d'amis définies dans les modèles de classe.

template<typename ...Args> 
class Entity { 
    template<typename ...ArgsInner> 
    class InnerEntity { 

    }; 

    template<typename ...ArgsAInner, typename... ArgsBInner> 
    > friend auto my_func(
     InnerEntity<ArgsAInner...> A 
     , InnerEntity<ArgsBInner...> B 
) -> tuple<decltype(A), decltype(B)> { 
     return make_tuple(A, B); 
    } 

}; 

Vous pouvez également déclarer une typedef membre InnerEntity qui spécifie le type de la classe externe, et de formuler my_func en termes de cela, de sorte que SFINAE peut trier pour les non-membres.

template<typename ...Args> 
class Entity { 
    template<typename ...ArgsInner> 
    class InnerEntity { 
    typedef Entity outer_entity; 
    };  
}; 

template<typename A, typename B, typename Result> 
struct require_entity { }; 

template<typename ...ArgsA, typename ...ArgsB, typename Result> 
struct require_entity<Entity<ArgsA...>, Entity<ArgsB...>> { 
    typedef Result type; 
}; 

template<template<typename...> class AInner, template<typename...> class BInner, 
     typename ...ArgsAInner, typename ...ArgsBInner> 
> auto my_func(
     AInner<ArgsAInner...> A 
    , BInner<ArgsBInner...> B 
) -> typename require_entity< 
     typename AInner<ArgsAInner...>::outer_entity, 
     typename BInner<ArgsBInner...>::outer_entity, 
      tuple<decltype(A), decltype(B)>>::type 
{ 
    return make_tuple(A, B); 
} 

Bien sûr, vous n'avez pas besoin que template<typename...> class AInner chose si vous n'avez pas besoin d'accéder aux ArgsAInner types, comme dans le my_func ci-dessus. Dans un tel cas, vous feriez mieux d'accepter typename AInner et ont moins à écrire. La SFINAE veillera toujours à ce que seule la bonne chose soit acceptée.

+0

Je vois. Peut-être que je laisse des informations pertinentes ici, je suis encore un peu confus sur pourquoi ce qui précède ne fonctionnerait pas. L'entité a des sous-classes qui définissent ce que serait Args, et elle a une fonction membre pour créer des objets InnerEntity, et cette fonction membre accepte et transmet les arguments pour InnerEntity en tant que pack. Compte tenu de cela, le compilateur ne devrait-il pas être capable de déduire les différents packs Args, puisqu'ils seraient tous connus au moment de la compilation? –

+1

@pheadbaq 'Entity' pourrait être spécialisé pour certains arguments tels que' InnerEntity' est une chose complètement différente de celle des autres arguments.En général, pour un paramètre de fonction de modèle de fonction si vous avez 'typename T :: foo', alors' T' ne peut pas être déduit par ce paramètre. –

+0

Après avoir mâché votre réponse et mon propre code un peu plus, je ne pense pas que la solution friend fonctionnera après tout, parce que ThingA et ThingB (voir mon code édité) n'héritent pas du même modèle Entity. On dirait que le nom de fichier AInner + SFINAE fonctionnerait bien cependant. Merci! –

Questions connexes