2017-08-22 3 views
1

Suite à l'avis de @Jonas dans un previous question, je définis une classe modélisée pour contenir un conteneur arbitraire (vector, set, map, etc) d'un containee arbitraire (string, tree , etc). Jusqu'à présent, ma définition est comme ceci:Spécialisation de la fonction dans la définition du modèle avec les paramètres du modèle de template

template <template <typename...> class Container, typename Containee = std::string, typename... extras> 
class Lemario 
{ 
public: 
    typedef typename Container<Containee, extras...>::iterator iterator; 
    typedef typename Container<Containee, extras...>::const_iterator const_iterator; 
    // Use 'Containee' here (if needed) like sizeof(Containee) 
    // or have another member variable like: Containee& my_ref. 
    Container<Containee, extras ...> mTheContainer; 
    int loadContainees(const char *filename) { 
     Containee w, line; 
     // do some stuff here 
    } 
    void appendContainee(const Containee &__x); 
}; 

Maintenant, je peux définir des méthodes les deux en ligne (comme loadContainees) l'extérieur de la définition du modèle. Extérieur:

template <template <typename...> class Container, typename Containee, typename... extras> 
Containee Lemario<Container, Containee, extras...>::transform_word(const  Containee& word) const 
{ 
    Containee result; 
    return result; 
} 

Jusqu'à présent, si bon. Mais maintenant je veux spécialiser une méthode pour ajouter un conteneur au conteneur comme vecteur, carte, arbre, utiliser différentes méthodes. J'essaie donc de se spécialiser std :: vector:

template <template <typename...> class Container, typename Containee, typename... extras> 
void Lemario<std::vector, Word>::appendContainee(const Word & word) 
{ 
    mTheContainer.push_back(word); 
} 

Mais je reçois l'erreur suivante:

error: prototype for ‘void Lemario<std::vector, gong::Xtring>::appendContainee(const Word&)’ does not match any in class ‘Lemario<std::vector, gong::Xtring>’ 

En dehors de cela, je peux ¿spécialiser uniquement le conteneur, std :: vecteur, mais laissez le conteneur non spécialisé?

template <template <typename...> class Container, typename Containee, typename... extras> 
void Lemario<std::vector, Containee>::appendContainee(const Containee & word) 
{ 
    mTheContainer.push_back(word); 
} 
+0

Si je me souviens bien, vous ne peut pas spécialiser la fonction du modèle de classe séparément de la classe elle-même –

+0

Ceci est un problème XY typique. La bonne réponse à votre question précédente était celle de Toby Speight. Vous n'avez pas besoin de cette structure compliquée ici. – Walter

+1

** ERREUR ** 'Word' n'est pas défini. – Walter

Répondre

1

Le problème est que votre implémentation de appendContainee comporte des erreurs de syntaxe. La bonne façon de se spécialiser la fonction pour std::vector et Word est de l'écrire comme ceci:

template <> 
void Lemario<std::vector, Word>::appendContainee(const Word & word) 
{ 
    mTheContainer.push_back(word); 
} 

Demo


Cette approche exige cependant que vous vous spécialisez entièrement la fonction à chaque fois, ce qui signifie que vous avoir à spécifier à la fois le conteneur et le type. Vous ne voulez probablement vous spécialiser que pour std::vector et pas aussi pour Word.

Ce problème est classiquement résolu avec un motif étiqueté. Dans le dispatch étiqueté, nous transformons la spécialisation en un problème de surcharge en introduisant des méthodes auxiliaires qui seront sélectionnées correctement par surcharge.

Nous créons un struct imbriqué basé sur un modèle vide au sein de notre classe:

private: 
    template<class...> 
    struct Lemario_tag{}; 

Et puis écrire private méthodes d'aide que la méthode de point d'entrée appellera:

template<class T> 
void appendContaineeHelp(const Containee &x, Lemario_tag<T>) 
{ 
    static_assert(sizeof(T) == 0, "No specialization exists for this container"); 
} 

void appendContaineeHelp(const Containee &x, Lemario_tag<std::vector<Containee, extras...>>) 
{ 
    mTheContainer.push_back(x); 
} 

Le premier est un fourre-tout cela provoquera une erreur de compilation si vous essayez de l'appeler avec un conteneur non spécialisé.Le second est spécialisé pour un std::vector.

Nous définissons notre publicappendContainee comme si (passe-par simple):

void appendContainee(const Containee &x){ 
    appendContaineeHelp(x, Lemario_tag<Container<Containee, extras...>>{}); 
} 

Nous pouvons utiliser notre récipient comme ceci:

Lemario<std::vector, std::string> vecString; 
Lemario<std::vector, int> vecInt; 
vecString.appendContainee("foo"); 
vecInt.appendContainee(1); 

Lemario<std::set, int> set_int; 
// set_int.appendContainee(1); // compiler error 

Better Demo

+0

La première approche fonctionne très bien pour moi. La seconde est bien au-delà de mes connaissances et de mes exigences, mais de toute façon, cela me fait penser. –

1

Vous pouvez vous spécialiser la fonction à l'aide std::enable_if (et SFINAE), voici un exemple, online.

template <template <typename...> class Container, typename Containee = std::string, typename... extras> 
class Lemario 
{ 
public: 
    typedef typename Container<Containee, extras...>::iterator iterator; 
    typedef typename Container<Containee, extras...>::const_iterator const_iterator; 
    // Use 'Containee' here (if needed) like sizeof(Containee) 
    // or have another member variable like: Containee& my_ref. 
    Container<Containee, extras ...> mTheContainer; 
    int loadContainees(const char *filename) { 
     Containee w, line; 
     // do some stuff here 
     return 0; // To make the compiler happy 
    } 

    template <typename Containee2> // For SFINAE to work 
    typename std::enable_if<std::is_same<Container<Containee2, extras...>, std::vector<Containee2, extras...>>::value>::type 
    appendContainee(const Containee2 & word) 
    { 
     mTheContainer.push_back(word); 
    } 

    template <typename Containee2> // For SFINAE to work 
    typename std::enable_if<std::is_same<Container<Containee2, extras...>, std::deque<Containee2, extras...>>::value>::type 
    appendContainee(const Containee2 & word) 
    { 
     mTheContainer.push_back(word); 
    } 
}; 

Exemple

int main() 
{ 
    Lemario<std::vector, int> foo; 
    std::cout << foo.mTheContainer.size() << std::endl; 
    foo.appendContainee(1); 
    std::cout << foo.mTheContainer.size() << std::endl; 

    Lemario<std::deque, int> bar; 
    std::cout << bar.mTheContainer.size() << std::endl; 
    bar.appendContainee(1); 
    std::cout << bar.mTheContainer.size() << std::endl; 
} 
+0

Je suis sûr que cette approche fonctionne également, mais celle de @AndyG est ce que je cherchais. –

+1

@HolaMundo Ok, mais je ne vois pas comment cela (sa première solution) accomplit: * puis-je spécialiser le conteneur, 'std :: vector', mais laisser le conteneur non spécialisé? * C'est exactement ce que ma réponse fait. – Jonas

+0

vous avez raison, c'était un requeriment que j'ai ajouté comme un bonus juste par curiosité, mais en réalité je n'en avais pas besoin. –