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

  1. #1
    Membre actif
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Points : 284
    Points
    284
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    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 averti

    Inscrit en
    Juillet 2008
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 186
    Points : 350
    Points
    350
    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
    Points : 13 017
    Points
    13 017
    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 éclairé

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    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 émérite
    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
    Points : 2 799
    Points
    2 799
    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 actif
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Points : 284
    Points
    284
    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 averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    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
    Membre émérite
    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
    Points : 2 799
    Points
    2 799
    Par défaut
    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 ?
    C'est exactement ce que j'ai dit, et ça marche (dans un std::list<Interface*>).

    Est-ce que tu peux détailler un peu plus ce qui ne marche pas ?

    As-tu bien écrit ? :

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

  10. #10
    Membre actif
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Points : 284
    Points
    284
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    C'est exactement ce que j'ai dit, et ça marche (dans un std::list<Interface*>).

    Est-ce que tu peux détailler un peu plus ce qui ne marche pas ?
    C'est parce que je ne pensais qu'à un std::list<Interface &>, pour ne pas avoir à gérer la mémoire.
    Pas très malin sur ce coup :-/

  11. #11
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Les références sont des allias sur des objets existants. Elle n'ont pas d'adresse propre, impossible de les mettre dans un conteneur alors.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    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

  13. #13
    Membre actif
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Points : 284
    Points
    284
    Par défaut
    Je suis désolé que cela prête ainsi à confusion, il aurait été plus clair que j'attende encore une semaine avant de poser mes questions.

    Mon code quasiment est prêt; j'y fait régulièrement quelques retouches mais je n'en changerai plus l'architecture.

    J'ai croisé quelques problèmes d'organisation au cours du développement, que j'ai contournés au mieux avec mes idées du moment.
    Je souhaite donc avoir des avis de gens différents, voir plus compétents.
    Comme au final, ce sont bien des questions d'architecture et pas des questions spécifiques au solitaire, je pose la question de façon abstraite. Ce n'est pas pour cacher ma demande d'aide.

  14. #14
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Pour par exemple stocker indifféremment des MaClasseTemplate<A> et des MaClasseTemplate<B>, tu fais dériver la classe template d'une classe de base.

    Après, il faut voir si c'est vraiment une bonne solution. Je pense que je commence à radoter, mais l'héritage ce n'est pas un outil à tout faire du C++. Il est sur-utilisé.

  15. #15
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Alp Voir le message
    Je pense que je commence à radoter, mais l'héritage ce n'est pas un outil à tout faire du C++. Il est sur-utilisé.
    D'où, je pense que ma proposition avec boost::variant est plus 'élégante' (question de gout bien sûr) et c'est pas si lourd que ça

  16. #16
    Membre actif Avatar de babar63
    Homme Profil pro
    Développeur jeux vidéos/3d Temps réel
    Inscrit en
    Septembre 2005
    Messages
    241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Développeur jeux vidéos/3d Temps réel

    Informations forums :
    Inscription : Septembre 2005
    Messages : 241
    Points : 207
    Points
    207
    Par défaut
    Citation Envoyé par Alp
    Pour par exemple stocker indifféremment des MaClasseTemplate<A> et des MaClasseTemplate<B>, tu fais dériver la classe template d'une classe de base.

    Après, il faut voir si c'est vraiment une bonne solution. Je pense que je commence à radoter, mais l'héritage ce n'est pas un outil à tout faire du C++. Il est sur-utilisé.
    Surtout que dans ce cas et niveau conceptuel, il n'y a absolument aucun sens à vouloir utiliser l'héritage si c'est juste pour pouvoir les stocker dans le même container... c'est comme ça que peuvent commencer les problèmes de conception qui en entrainent par la suite des bien plus difficile à résoudre à force de "rafistolage maladroit". La solution de 3DArchi est en effet bien plus logique (à mon sens).
    - hp pavillon dv7
    - intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
    - nVidia GeForce 9600M GT
    - mémoire vive : 3.0Go

  17. #17
    Membre actif
    Inscrit en
    Décembre 2003
    Messages
    272
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 272
    Points : 284
    Points
    284
    Par défaut
    Citation Envoyé par babar63 Voir le message
    Surtout que dans ce cas et niveau conceptuel, il n'y a absolument aucun sens à vouloir utiliser l'héritage si c'est juste pour pouvoir les stocker dans le même container... c'est comme ça que peuvent commencer les problèmes de conception qui en entrainent par la suite des bien plus difficile à résoudre à force de "rafistolage maladroit". La solution de 3DArchi est en effet bien plus logique (à mon sens).
    Je prend note, "ne pas sur-estimer l'héritage".
    Facile à retenir, c'est comme avec Papi.

    (Dans mon cas, il y aurait eu une interface commune. Je pense donc que l'héritage se défendait.)

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