IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Template et héritage


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Par défaut Template et héritage
    Bonjour,
    Je constate que l'utilisation de template gêne l'utilisation de l'héritage et polymorphisme :
    • D'une part, ClasseTemplate<B> ne dérive pas de ClasseTemplate<A> quand B dérive de A
    • D'autre part, je ne peux pas utiliser de de conteneur pour manipuler à la fois des A et des B


    Je peux contourner le second point avec une classe contenant un pointeur vers A (un genre de pimpl), qui doit gérer les copies.
    Pour l'autre problème, je ne voit pas quoi faire.

    Connaissez vous des solutions à tout cela ?

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Pour le premier point je suis généralement content que ce ne soit pas la cas. Pourrais-tu me donner un exemple où ça gênerait ?

    Dans les cas où l'on aimerait une certaine interopérabilité (shared_ptr<A> a; shared_ptr<B> b; a=b, on résoud souvent ça en ajoutant un constructeur templatée qui transfère la compatibilité du type template sur la compatibilité du paramètre.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre très actif

    Inscrit en
    Juillet 2008
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 186
    Par défaut
    Bonjour,

    Pour répondre à ta première question : "Pourquoi le dervrait-il ?"

    Tu peux le faire simplement toi même, avec une spécialisation. Pourquoi vouloir l'imposer à tous ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {};
    class B : public A {};
     
    template <class T>
    class ClassTemplate
    {};
     
    template <>
    class ClassTemplate<B> : public ClassTemplate<A>
    {};
    Didier

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Il est étrange de vouloir dériver T<B> de T<A> au prétexte que A dérive de B. Une classe template offre des services indépendants du type avec lequel elle est instanciée. Que signifie sémantiquement T<B> dérive de T<A>??? Ca sent le problème de conception...

    Pour ton second point, tu dois pouvoir utiliser Boost.variant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <boost/variant.hpp>
     
    template<class T>
    class Templ
    {
    public:
       void QuiSuisJe()const {T::QuiSuisJe();}
    };
     
    class A
    {
    public:
       static void QuiSuisJe(){std::cout<<"Je suis un A\n";}
    };
     
    class B
    {
    public:
       static void QuiSuisJe(){std::cout<<"Je suis un B\n";}
    };
     
    class visiteurQuiSuisJe : public boost::static_visitor<void>
    {
    public:
       template<class T>
        void operator()(T t_) const
        {
           t_.QuiSuisJe();
        }
     
    };
     
    struct QuiSuisJe
    {
       void operator()(boost::variant<Templ<A>,Templ<B> > const &var_) const
       {
          boost::apply_visitor(visiteurQuiSuisJe(),var_);
       }
    };
     
    int main()
    {
       std::vector<boost::variant<Templ<A>,Templ<B> > > vecteur_de_a_ou_b;
       vecteur_de_a_ou_b.push_back(Templ<A>());
       vecteur_de_a_ou_b.push_back(Templ<A>());
       vecteur_de_a_ou_b.push_back(Templ<B>());
       vecteur_de_a_ou_b.push_back(Templ<B>());
       vecteur_de_a_ou_b.push_back(Templ<A>());
       std::for_each(vecteur_de_a_ou_b.begin(),vecteur_de_a_ou_b.end(),QuiSuisJe());
       return 0;
    }

  5. #5
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Ca peut paraitre utile quand on écrit des templates de smart pointers.
    Mais comme l'a dit JolyLoic, on peut s'en passer avec un peu de bidouille :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    template<class T>
    Pointer
    {
    public : 
     
        // ...
     
        template<class N>
        operator Pointer<N>()
        {
            return Pointer<N>(pValue_);
        }
     
    private :
     
        T* pValue_
    };
    Mais il me semble qu'il y avais quelques limitations... je ne les ai plus en tête.

  6. #6
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Il est étrange de vouloir dériver T<B> de T<A> au prétexte que A dérive de B. Une classe template offre des services indépendants du type avec lequel elle est instanciée. Que signifie sémantiquement T<B> dérive de T<A>??? Ca sent le problème de conception...
    En fait, c'est une erreur de conception assez répandue. L'idée qu'un array<B> dérive d'un array<A> si B dérive de A, qu'on retrouve en java, csharp, par exemple.

    C'est une grosse erreur, et ça viole le LSP. Ce qui est dingue, c'est que des gens brillants la fassent encore.

    Pour ton premier problème, tu peux passer par une interface (faire dériver ton template d'une interface, ainsi, T<B> et T<A> auront une interface commune, donc tu pourras avoir un conteneur de T<B> et T<A>). Ou alors, boost::any ou boost::variant.

  7. #7
    Membre expérimenté
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Par défaut
    Alors pour commencer, je suis tout à fait d'accord : en réfléchissant un peu, on se rend vite compte qu'il est plus sain que ces 2 point ne marchent pas.

    J'accepte les accusations d'erreur de conception ! Il faut bien apprendre

    C'est pour implémenter l'exploration du solitaire (défi en cours). Voici mon cheminement :
    J'ai une classe EnsembleDeCases qui décrit les emplacements des cases du plateau, ou encore les emplacements des pions.
    Et une classe Solitaire qui contient 2 EnsembleDeCases (pour les cases existantes et pour celles occupées) qui fera le travail (explorer les chemins possibles vers la solution).

    Je me propose d'avoir différentes variantes pour EnsembleDeCases.
    Solitaire ne peut pas dériver de EnsembleDeCases puisqu'il y en a 2 à l'intérieur. J'en fait donc un template.

    En même temps, je souhaite définir une interface globale pour les EnsembleDeCases, que je fait donc dériver d'une classe abstraite.
    Mais au final, ça ne sert à rien, car je ne peux pas manipuler de Solitaire<EnsembleDeCasesAbstrait> contenant soit Solitaire<EnsembleDeCases1> soit Solitaire<EnsembleDeCases2>.

    Alors que j'aurais aimé écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Solitaire<EnsembleDeCasesAbstrait> *monSolitaire;
    if (choixDImplementation1) {
        monSolitaire = new Solitaire<EnsembleDeCases1>;
    } else if (choixDImplementation2) {
        monSolitaire = new Solitaire<EnsembleDeCases2>;
    } else {
        monSolitaire = new Solitaire<EnsembleDeCases3>;
    }
    monSolitaire->init(unFichier);
    monSolitaire->auBoulot();
    if (monSolitaire->pasDErreur()) {
        monSolitaire->faitVoirCeQueTuAsFait(std::cout);
    }
    delete monSolitaire;
    Résultat, le code init/auBoulot/... est à recopier pour chaque implémentation. C'est nul.

    Bon, on s'en sort très bien, soit en utilisant une fonction template, soit en mettant des pointeurs dans Solitaire au lieu d'en faire un template.
    Mais au final, je n'ai pas réussi à mélanger template et polymorphisme. Surprenant pour 2 fonctionnalités phare du langage.


    Citation Envoyé par 3DArchi Voir le message
    Pour ton second point, tu dois pouvoir utiliser Boost.variant :
    C'est lourd (boost nécessaire, une classe template supplémentaire) et je regrette que cela ne prenne pas en compte la relation entre A et B.
    Mais ça répond à la question

    Citation Envoyé par white_tentacle Voir le message
    C'est une grosse erreur, et ça viole le LSP.
    LSP ?

    Pour ton premier problème, tu peux passer par une interface (faire dériver ton template d'une interface, ainsi, T<B> et T<A> auront une interface commune, donc tu pourras avoir un conteneur de T<B> et T<A>).
    Heu, si j'ai bien compris, tu dis que si A et B dérivent de Interface, je peux les mettre dans un std::list<Interface>.
    C'est bien ce que j'avais en tête mais qui ne marche pas.
    Que voulais tu dire, alors ?

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Par défaut
    Citation:
    Envoyé par white_tentacle Voir le message
    C'est une grosse erreur, et ça viole le LSP.
    LSP ?
    http://en.wikipedia.org/wiki/Liskov_...tion_principle

  9. #9
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,
    Citation Envoyé par Ulmo Voir le message
    C'est pour implémenter l'exploration du solitaire (défi en cours).
    <snip>
    Je conçois que ce soit un bon moyen d'apprendre, mais, si c'est pour t'assurer d'une place au défi, ce n'est pas très honnête de poser la question sur le forum

    Enfin...

    Un conseil que l'on pourrait te donner est de veiller à la délégation des tâches.

    Pour la plupart des jeux "de pions", l'idéal est de partir sur:
    • Une classe "case" capable de dire si elle est libre ou non et si elle accepte
      de recevoir un pion (et/ou d'être survolée)
    • une classe "pion" capable de donner les coordonnées de la case sur laquelle il se trouve
    • une classe "joueur" pour "l'intelligence"
    • une classe "partie" pour gérer les différents joueurs
    • une classe "plateau" qui met les deux premières classes en relation et
      qui reçoit ses instructions (questionnement des pions et des cases et déplacement des pions) de la classe partie

    Dans le cadre du solitaire, toutes les classes ne sont pas forcément nécessaires
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Template et héritage privé.
    Par 3DArchi dans le forum Langage
    Réponses: 6
    Dernier message: 18/09/2009, 09h36
  2. Template et héritage
    Par rooger dans le forum Langage
    Réponses: 5
    Dernier message: 22/07/2008, 13h48
  3. Foncteur, classes templates et héritage
    Par Floréal dans le forum C++
    Réponses: 8
    Dernier message: 17/06/2007, 21h56
  4. Template ou héritage
    Par bolhrak dans le forum Langage
    Réponses: 6
    Dernier message: 22/12/2006, 11h22
  5. patron, templates et héritages!
    Par kleenex dans le forum C++
    Réponses: 4
    Dernier message: 05/06/2006, 22h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo