Partage
  • Partager sur Facebook
  • Partager sur Twitter

erreur de compile sur vector

Sujet résolu
    15 août 2018 à 17:55:20

    sur mon code pour essayer les template j ai une erreur de compile sur la ligne de parametrage du constructeur:

    error expected ')' before '<' token dans la classe de mon template(c est bien un .h)

    le code:

    #ifndef PONDERATE_SUM_H
    #define PONDERATE_SUM_H
    #include<vector>
    
    template<typename T,typename S,typename R>
    class Ponderate_Sum
    {
        public:
             Ponderate_Sum(vector<T> const& tab_poids,vector<S> const& tab_param)
            {
                vector<T>::iterator it1;
                vector<S>::iterator it2=tab_param.begin();
                for(it1=tab_poids.begin();((it1!=tab_poids.end())&&(it2!=tab_param.end());it1++)
                {
                    m_result+=(R)((*it1)* (*it2));
                }
            }
            R resultat(){
            return m_resultat;
            }
            virtual ~Ponderate_Sum();
    
        protected:
    
        private:
            R m_result;
    };
    
    #endif // PONDERATE_SUM_H
    



    je trouve pas ou est l erreur.

    si quelqu un sait ...merci.

    -
    Edité par GouyFred 15 août 2018 à 18:30:13

    • Partager sur Facebook
    • Partager sur Twitter
      15 août 2018 à 18:29:10

      Salut,

      virtual ~Ponderate_Sum();

      Dans une classe template... Ooops...

      Une chance que tu n'en as pas fait une fonction virtuelle pure, car tu aurais foutu un sérieux boxon :D

      Ceci dit, la manière dont tu essaye d'utiliser cette classe et -- surtout -- les erreurs indiquées par le compilateur, même si elles sont souvent cryptiques lorsque l'on manipule des formes template représentent un point de départ indispensable pour pouvoir déterminer d'où vient l'erreur.

      Car je présumes que ton problème actuel est de compiler ton code, et non l'erreur de logique qui se cache derrière ces quelques lignes de code

                  vector<T>::iterator it1;
                  vector<S>::iterator it2=tab_param.begin();
                  for(it1=tab_poids.begin();((it1!=tab_poids.end())&&(it2!=tab_param.end());it1++)

      (ben oui, it2 semble n'être jamais modifié, dés lors, pourquoi l'utiliser dans la condition d'entrée dans la boucle ? )


      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        15 août 2018 à 20:14:00

        Pour l'erreur en elle-même, c'est probablement parce qu'il manque std:: devant vector.

        Pour les prochaines erreurs, il faut utiliser typename pour avoir un type d'une classe dépendant d'un type template (vector<T>::iterator -> typename vector<T>::iterator).

        Type d'itérateur qui est au passage faux puisque le vector est constant et que seul const_iterator est accessible. C'est franchement un cas ou auto est largement préférable. Ce qui enlèverait cette initialisation en 2 temps particulièrement discutable.

        Et sinon, pourquoi une classe ? Comprends-tu vraiment ce qu'implique virtual ? Pourquoi pas une fonction ? Pourquoi pas std::inner_product ?

        • Partager sur Facebook
        • Partager sur Twitter
          15 août 2018 à 21:07:45

          Merci pour ta réponse jolink .

          Toutes tes prédictions sur les erreurs signalées par le compilateurs se sont avérées exactes.

          Juste la derniere remarque ("Type d'itérateur qui est au passage faux puisque le vector est constant et que seul const_iterator est accessible. C'est franchement un cas ou auto est largement préférable. Ce qui enlèverait cette initialisation en 2 temps particulièrement discutable.")

          je ne vois pas en quoi le fait de declarer le vecteur constant m empeche d acceder aux valeurs pour les lire(pourtant en effet a la compile j ai :

          error:conversion from std::vector<int>::const_iterator{aka..ect..}le ect pour un truc que j essaierai pas de comprendre direct vu que ca l air de rentrer dans les entrailles du code.

          et Sinon pour "répondre a pourquoi une classe? comprends tu vraiment ce qu implique virtual?"

          la réponse a la premiere question est si je suis amené a codé ca c est pour expérimentées les notions de c++ que j essaie d ingérer.(je débute quoi).et a la seconde la réponse est : non pas du tout ^^.

          en tout cas merci pour ta réponse. je suis resté bloqué au moins deux heures cette aprem sans voir ou ca clochait.

          du coup j ai fait comme t as dit: j ai déclaré un const_iterator..bah ca marche.Je comprends pas trop ce qui se passe pour que ca marche pas avec un iterateur standard mais avec lui oui mais bon faut bien dire qu a part la description "un pointeur spécial" je ne sais pas grand chose de ces betes. apres j ai pour l heure bien assez de notion a emboiter les unes aux autres sans en rajouter.J en ai deja pour une vie.

          -
          Edité par GouyFred 15 août 2018 à 21:48:17

          • Partager sur Facebook
          • Partager sur Twitter
            15 août 2018 à 21:54:25

            >si je veux utiliser un iterateur j ai pas le droit de le passer en const ?

            Le problème est de vouloir un itérateur non-constant sur un vector constant (std::vector::iterator vs std::vector::const_iterator). Si tu lis bien l'erreur, les 2 types présentés par le compilateur ne sont pas les même, l'un possède des const, l'autre non. C'est pour cela qu'avec les itérateurs, il est beaucoup plus simple de faire auto it = tab.begin() et laisser le compilateur déduire le type de it.

            > et sinon j ai un probleme avec les destructeur dans les templates...faut en declarer un?si oui faut un cpp, ou le declarer das le .h sans reference a un typename??

            Le fait que la classe soit template ne change pas les règles sur le fait d'avoir un destructeur ou non. Pour le destructeur, c'est simple, si tu n'en as pas besoin, ne le met pas.

            La règle de séparation .hpp/.cpp pour les fonctions membres est selon l’envie. Mais généralement les implémentations sont dans le .cpp et les prototypes de fonction dans le .h. Même si l'implémentation des petites fonctions sont souvent mises dans le .h par flemme ou par préférence.

            Par contre, pour les templates, jamais de .cpp, tout dans le .h. Même si on peut faire la séparation déclaration/implémentation pour les fonctions et classes templates, tout doit au final être visible dans les .h.

            Pour virtual, à moins de vouloir du polymorphisme dynamique (héritage), ne le met pas. Par contre, si une classe possède au moins une fonction virtuelle, il faut aussi mettre un destructeur virtuel. S'il ne fait rien, alors simplement le mettre comme =default (virtual ~MyClasse() = default;).

            • Partager sur Facebook
            • Partager sur Twitter
              15 août 2018 à 23:28:36

              GouyFred a écrit:

              error:conversion from std::vector<int>::const_iterator{aka..ect..}le ect pour un truc que j essaierai pas de comprendre direct vu que ca l air de rentrer dans les entrailles du code.

              Comme je l'ai dit plus haut, les erreurs renvoyées par le compilateur sont parfois cryptiques et souvent très nombreuses :P.

              Mais, si tu veux évoluer, il va pourtant apprendre à essayer de les comprendre, ou, à tout le moins, essayer de t'habituer à trouver "celle qui te mettra sur la voie" :p

              Car, même si elles sont particulièrement cryptiques, les erreurs qu'il te transmet on avant tout pour objectif de te faire comprendre ce qui chiffonne le compilateur.

              Tu peux, par exemple, biensouvent ignorer les "(AKA ....)" qui indiquent le type qui a spécifiquement été utilisé comme paramètre template (et parfois comme alias de type à l'intérieur de la classe que tu manipules)

              Sauf (c'est pas de bol pour toi, sur ce coup), justement si l'erreur porte sur conversion from std::vector<int>::const_iterator, car le compilateur essaye justement de te dire qu'il a rencontré un problème en essayant de convertir "quelque chose" (un std::vector<int>::const_iterator dans le cas présent) en "autre chose".

              Cet autre chose se trouve sans doute juste après le AKA et sera -- sans doute -- lui aussi suivi d'un AKA, parce qu'il s'agira vraisemblablement (aussi bien pour le type d'origine que pour le type "de destination") d'un alias de type.

              Du coup, le fameux AKA (As Known As ou "aussi connu comme" en anglais) devrait te permettre de comprendre quelle conversion n'a pas pu s'effectuer correctement.

              Mais ca, ca ne sera l'information importante que de la première erreur que le compilateur t'aura indiquée. Pour comprendre pourquoi cette conversion était nécessaire et surtout à quel endroit du code cette conversion aurait du s'effectuer, il va falloir aller voir un peu les autres

              Généralement, les message d'erreurs indiquent non seulement l'erreur qui s'est produite, mais aussi le fichier (et la ligne de ce fichier) dans lequel l'erreur survient.

              Une fois que tu sais "à peu près ce qui se passe (nous disions donc que, dans le cas présent, le compilateur se plaignait de ne pas pouvoir convertir un std::vector<int>::const_iterator en "autre chose"... Penses à vérifier en quoi il aurait voulu le convertir :D ) tu dois te mettre à la recherche d'erreurs qui surviennent dans les fichiers que tu as écrit.

              Car la bibliothèque standard est suffisamment malmenée et suffisamment étudiée par largement assez de monde que pour qu'on puisse partir du principe qu'elle ne contient pas d'erreur manifeste; ou du moins qu'elle ne contient très certainement pas du code qui risquerait d'amener à une erreur "aussi simple" qu'une conversion impossible.

              Si une telle erreur survient (et c'est malheureusement ton cas), on peut donc en conclure que c'est parce que tu t'es vautré "quelque part" en essayant d'utiliser une fonctionnalité de la bibliothèque standard.

              Mais le compilateur est vraiment sympa, parce que, parmi toutes ces erreurs qui n'ont ni queue ni tête (du moins, avant que l'on ne s'habitue à les comprendre), il y en aura forcément au moins une qui aura trait à un des fichier que tu as écrit.

              Et cette erreur devrait non seulement te permettre de trouver le fichier (et la ligne) dans lequel elle survient, mais sans doute aussi te donner une idée de la portion de code qui est en cause et, surtout, te dire ce qui a provoqué l'erreur.

              Elle te parlera peut être d'initialisation, de conversion ou encore de création en te disant quelque chose comme "while initializasing / converting / creating" XXX.

              En remettant les informations glanées de gauche et de droite, tu devrais alors comprendre que le code que tu as sous les yeux essaie de créer XXX en convertissant un YYY (un std::vector<int>::const_iterator, dans le cas présent) en...autre chose (qui t'a également été décrit par le compilateur).

              Et, a priori, tu devrais donc pouvoir "corriger le tir", par exemple, en faisant en sorte que ce XXX soit bel et bien du type dont tu diposes pour l'instant (un std::vector<int>::const_iterator, dans le cas présent), ou en faisant en sorte de disposer... d'un élément qui corresponde au type de XXX.

              Mais bon, je peux te rassurer... il faut parfois "un certain temps" avant d'arriver à décrypter ce genre d'erreur.  Et, à partir du moment où l'on sait faire la différence entre les erreurs, les avertissement et les informations (mais bon, c'est écrit au tout début :D ), il n'est même pas forcément indispensable d'arriver à les décrypter entièrement : on peut "très souvent" se contenter d'en ignorer "une bonne partie" et ne s'intéresser qu'à "quelques points intéressants".

              Malheureusement, il n'y a pas de liste exhaustive des points auxquels faire attention lorsqu'on essaye de décrypter une erreur émise par le compilateur. Et, bien sur, elles seront le plus souvent émises en anglais :waw:.  Mais, l'un dans l'autre, il "suffit" généralement d'essayer "régulièrement" d'en comprendre "un peu plus" pour s'habituer à les décrypter ;)

              GouyFred a écrit:

              et Sinon pour "répondre a pourquoi une classe? comprends tu vraiment ce qu implique virtual?"

              Ah, ben oui... Donc, en gros, tu as vu "quelque part" qu'un destructeur pouvait être virtuel, et, comme tu n'as pas peur d'user inutilement ton clavier et que tu trouvais que cela faisait beau avec la coloration syntaxique du code, tu as décidé de d'utiliser cette forme là, quoi ????

              Le fait de déclarer une fonction comme étant virtuelle répond à un besoin bien particulier.  Si tu ne sais pas encore à quel besoin bien particulier cela correspond (mais ne t'en fais pas, cela ne devrait pas tarder :D ), le mieux à faire est de ne pas encore essayer de l'utiliser.

              Car, si c'est une possibilité qui permet de répondre à un besoin bien particulier, il faut bien comprendre que c'est aussi une possibilité qui présente un cout à l'exécution bien particulier.

              Or, la philosophie du C++ a toujours été de ne pas te faire payer pour ce dont tu n'as pas besoin. Et le simple fait que tu ne saches pas encore à quoi sert ce mot clé semble indiquer que tu n'as encore besoin de l'utiliser.  Et donc, poses toi peut être la question "pourquoi devrais-je payer le cout d'utilisation d'une possibilité dont j'ignore tout, jusqu'à -- et y compris -- le cout que cette possibilité aura sur l'exécution de mon programme?"

              Tu en viendras surement à la conlusion que... tu n'as aucune raison de payer ce cout ;)

              GouyFred a écrit:

              la réponse a la premiere question est si je suis amené a codé ca c est pour expérimentées les notions de c++ que j essaie d ingérer.(je débute quoi).et a la seconde la réponse est : non pas du tout ^^.

              C'est --dans l'absolu -- une raison des plus valables...

              Mais, j'y mettrais peut-être un bémol, qui pourrait dépendre de la réponse à une autre question du coup : quelles sont les notions que tu essaye d'intégrer en faisant cela?

              Et surtout: de quelles bases disposes tu pour essayer d'intégrer ces notions?

              Car, si le souhait de s'améliorer ne peut qu'être très largement soutenu, notre rôle à nous -- vieux briscards -- est aussi régulièrement de "tempérer les ardeurs" et de rappeler qu'il faut "savoir marcher avant de vouloir courir".

              Je ne dis pas que c'est ton cas, mais, nous (les vieux briscards) avons quelque part la "responsabilité morale" de nous assurer que vous (les "débutants") vous astreigniez à des objectifs "accessibles".

              Si tu décidais demain de participer au marathon de New York, je me sentirais moralement tenu de te demander si tu estimes avoir l'entraînement nécessaire pour avoir au moins une chance de le terminer autrement que sur une civière. 

              Hé bien, ici, c'est pareil : parfois, certains objectifs sont "hors de portée", et il est sans doute préférable de le signaler à temps à celui qui s'est astreint à cet objectif pour lui permettre de "revoir ses prétentions à la baisse", en s'astreignant à des objectifs "plus à sa portées" qui le "prépareront d'avantage" pour lui permettre d'atteindre son objectif initial.

              Je ne dis pas que c'est ton cas particulier.  Je ne te connais pas assez pour juger de la sorte. Et c'est -- justement -- pour me permettre de me faire une idée de tes objectifs et de tes connaissances que je te pose cette question ;)

              GouyFred a écrit:

              du coup si je veux utiliser un iterateur j ai pas le droit de le passer en const?(ca serait ballot parceque en effet j ai pas l intention de le toucher).

              Attention, il y a une différence entre un const_iterator et un itérateur (ou une variable)  constant(e)...

              La notion d'itérateur est une notion dont le but est de te permettre de parcourir les éléments d'une collection.  Cette notion est représentée sous la forme de deux classes (en fait des alias de type, mais cela n'a que peu d'importance iciclairement distincte:

              • la classe /* std::vector<Type>::*/ iterator qui permet -- le cas échéant -- de modifier la valeur de l'élément de la collection auquel il fait référence et
              • la classe /* std::vector<Type>::*/const_iterator  qui ne permet d'accéder à l'élément qu'en "lecture seule" (on peut interroger l'itérateur pour connaitre la valeur de l'élément auquel il fait référence, mais on ne peut pas modifier  cette valeur)

              Il ne faut pas confondre ces deux classes avec la possibilité de déclarer des données comme étant constates: une donnée constante est une données pour laquelle on impose au compilateur de s'assurer que nous n'essayerons pas d'en modifier la valeur.

              C'est donc comme les papou papa, à pou, pas à pou et des papous pas papa à pou et pas à pou: Entre les deux possibilités que sont les iterator et les const_iterator et le fait de pouvoir disposer de données constante et de données non constantes, nous nous retrouvons avec quatre possibilité distinctes:

              • des données constante de type iterator;
              • des données non constante de type iterator;
              • des données constante de type const_iterator et
              • des données non constantes de type const_iterator.

              Les données constantes de type iterator sont "moyennement permissives" nous permettent de modifier la valeur de l'élément référencé par l'itérateur dont on dispose (parce que iterator nous offre un accès "lecture / écriture"), mais ne nous permettent pas de passer "à l'élément suivant" (parce que c'est une donnée constante)

              les données non constantes de type iterator sont les plus "permissives" car elles nous permettent non seulement de modifier la valeur de l'élément référencé par l'itérateur (parce que c'est iterator et non const_iterator) et de passer à "l'élement suivant" (parce que c'est une donnée non constante)

              les données constantes de type const_iterator sont "particulièrement restrictives" parce qu'elles ne nous premettent pas de modifier la valeur de l'élément référencé (parce que c'est un ... const_iterator),  et qu'elles ne nous permettent pas non plus de passer à l'élément suivant (parce que c'est une donnée constante)

              Et, enfin, les données non constante de type const_iterator sont "suffisemment permissives" pour bien des usages car elles nous permettent d'accéder "à l'élément suivant" (parce que c'est des données non constantes), mais ne nous permettent pas de modifier la valeur de l'élément référencé (parce que c'est un ... const_iterator).

              Comme tu le vois, chaque possibilité a ses avantages et ses inconvénients.  ( Hé oui, on peut trouver de très bonnes raisons pour avoir une donnée constante de type const_iterator ;) )

              Dans l'idéal, le choix de la donnée que tu vas manipuler devrait toujours te mener à utiliser la donnée qui t'offre "juste assez" de libertés pour faire ce que tu as à faire.  Si bien que, dans l'idéal, le choix (du moins permissif au plus permissif) devrait se faire dans l'ordre suivant:

              1. une donnée constante de type const_iterator : quand tu veux juste de connaitre la valeur de l'élément référencé
              2. une donnée non constante de type const_iterator : quand tu veux juste connaitre la valeur de plusieurs éléments de ta collection
              3. une donnée constante de type iterator : quand tu veux juste pouvoir modifier la valeur de l'élément référencé
              4. une donnée non constante de type iterator: quand tu veux pouvoir modifier la valeur de plusieurs des éléments de ta collection

              Il est toujours préférable de partir de la possibilité qui place le plus de restrictions possibles quant à la manière dont tu manipules une donnée, et de ne décider de choisir une solution "moins restrictive" que si les restrictions que tu t'es imposées t'empêchent définitivement de faire ce qui doit être fait ;)

              Enfin, on peut envisager de convertir une donnée de type iterator (qu'elle soit constante ou non n'a pas d'importance) en une donnée de type const_iterator, car cela ne fait "qu'augmenter les restrictions" quant à ce que l'on peut faire avec l'élément référencé.

              Par contre, on ne peut pas convertir une donnée de type const_iterator en une donnée de type iterator, car cela reviendrait à "lever des restrictions" que nous avions sans doute de bonnes raisons d'imposer à la base.

              Dans bien des cas, si le compilateur se plaint de ne pas pouvoir convertir un const_iterator en quelque chose, ce sera parce que tu lui demande de le convertir en iterator. Et il refusera de le faire parce que cela revient à "lever des restrictions" que tu avais toi-même imposées quant à l'utilisation que tu voulait pouvoir faire de l'élément référencé ;)

              • Partager sur Facebook
              • Partager sur Twitter
              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                16 août 2018 à 0:18:00

                D'abord merci,

                Ta réponse reprend de facon assez exhaustive des points tres obscurs pour moi.

                ca va me donner du grain a moudre.

                Ensuite en ce qui concerne le destructeur virtuel,

                j ai deux raison qui m ont poussé a faire ca...et qui faute de nouvel element sont les seuls dont je dispose.Du coup a moins d un grand coup de baguette sur la patte bah dans un an ou deux , faute d avoir reussi a mettre une representation adéquate de votre réserve ,je vais récidiver....

                Je donne les deux raisons:

                1.dans le cour sur le polymorphisme j ai lu:

                "Cela nous permet de formuler une nouvelle règle importante : un destructeur doit toujours être virtuel si on utilise le polymorphisme."

                (ok la ca s applique pas vu que je fais pas cette chose appelée polymorphisme, mais dans mon inconscient fébril ca a joué, d autant que de page en page ma mémoire est devenu myope si bien que le mot polymorphisme a pu 'glisser')

                2.la Chose qui a également pu faire glisser le mot polymorphisme de la table des négociations, c est qu étant un des 483 058754 padawans de mateo dans son cour de c++ ,j utilise code::block et lui par defaut (ou alors avec les reglages dont j ai hérité sans rien touché) il déclare toujours le destructeur comme virtuel.

                Du coup ni une ni deux en bon sophiste j ai transformé ces deux assertions avec mon shaker en "devant le destructeur ecrit virtual.

                -
                Edité par GouyFred 16 août 2018 à 0:25:32

                • Partager sur Facebook
                • Partager sur Twitter
                  17 août 2018 à 14:16:56

                  GouyFred a écrit:

                  "Cela nous permet de formuler une nouvelle règle importante : un destructeur doit toujours être virtuel si on utilise le polymorphisme."

                  Déjà, il devrait préciser qu'il parle du polymorphisme d'inclusion, car, le terme polymorphisme désigne simplement "la capacité d'une fonction à adapter son comportement aux circonstances et / ou  au type de données utilisées".

                  En cela, les fonctions surchargée (foo(int) Vs foo(double) ) et les fonctions génériques sont aussi des fonctions polymorphique,mais elles n'ont rien à voir dans la choucroute ici ;)

                  Le polymorphisme d'inclusion désigne -- quand à lui -- spécifiquement le fait qu'une fonction exposée par la classe mère dans le cadre d'un héritage public (au sens du LSP) peut adapter son comportement au type d'une classe dérivée lorsque l'on transmet une instance de la classe dérivée à une fonction qui s'attend à recevoir une (référence  sur une) instance de la classe de base.

                  Mais, en plus, ce n'est pas tout à fait vrai, car le destructeur de la classe de base ne devra être public et virtuel que si l'on envisage la possibilité de vouloir détruire une instance de n'importe quelle classe dérivée alors qu'elle n'est connue que comme étant du type de la classe de base.

                  Lorsque cette possibilité n'est pas nécessaire, nous pouvons tout à fait envisager de déclarer le destructeur (non virtuel) de la classe de base dans l'accessibilité protégée ;)

                  GouyFred a écrit:


                  2.la Chose qui a également pu faire glisser le mot polymorphisme de la table des négociations, c est qu étant un des 483 058754 padawans de mateo dans son cour de c++ ,j utilise code::block et lui par defaut (ou alors avec les reglages dont j ai hérité sans rien touché) il déclare toujours le destructeur comme virtuel.

                  C'est le problème des outils "trop simples": ils utilisent des modèles qui sont également particulièrement simples. 

                  Dans le cas présent, les auteurs de Codes::blocks (qui semblent meilleurs en C qu'en C++ :p ) sont partis de l'idée simple, mais courante (et pas forcément juste ) que les classes que nous créerons avec leur assistant interviendront forcément dans une hiérarchie de classes et que nous voudrons forcément pouvoir détruire les instances des classes dérivées en ne les connaissant que comme des instances de la classe de base.

                  Ces deux assertions sont fausses:

                  la première parce que la seule différence entre le mot clé class et le mot clé struct en C++ tient dans l'accessibilité par défaut des types ainsi déclarés : elle est privée pour le mot clé class et publique pour le mot clé struct.  Mais, autrement, il n'y a strictement aucune différence entre les deux ;)

                  La deuxième, c'est parce que, si tu crées une hiérarchie de classes, c'est très certainement parce que tu vas vouloir profiter de toutes les classes qui la composent.

                  Ainsi, si tu crées une hiérarchie de classes proche de

                  class Base{
                      /* ... */
                  };
                  
                  class Derivee1 : public Base{
                      /* ... */
                  };
                  
                  class Derivee2 : public Base{
                      /* ... */
                  };
                  /* ... */
                  class DeriveeN : public Base{
                      /* ... */
                  };

                  C'est très certainement parce que tu prévois d'utiliser des instances de Derivee1, Derivee2 ... DeriveeN.  Et donc le destructeur de la classe Base ne devra être public et virtuel que

                  • si tu veux pouvoir créer des instances de Base ET
                  • si tu  prévois de créer des collections de pointeurs sur des instances de Bases qui pointeront en réalité sur des instances de Derivee1, Derivee2 ... DeriveeN.

                  Mais, si tu peux n'avoir que des fonctions qui profiteront du polymorphisme parce qu'elles prennent des (référence sur des) objets "connus pour être du type de base" comme, par exemple

                  void foo(Base /* const */ & b){
                       /* ...*/
                  }

                  et que de l'autre coté, toutes les instances que  tu sont connues pour être de leur type réel,  par exemple sous une forme proche de

                  int main(){
                      Derivee1 d1;
                      Derivee2 d2;
                      /* ... */
                      DeriveeN dN;
                  }

                  le destructeur de la classe de base n'a absolument aucune raison d'être public et virtuel: il peut tout aussi bien être du protégé et non virtuel.  Surtout si la classe Base est une classe abstraite.

                  De plus, tout ce que je viens d'écrire n'a de sens que lorsque l'on utilise une approche exclusivement orientée objets.

                   Or, du fait que tu utilises les template pour ta classe ponderate_sum, tu te retrouve à utiliser une approche générique.  En gros, tu t'es dit quelque chose comme : je ne sais pas encore quels seront les types réels des données que je vais utiliser, mais je sais par contre parfaitement comment je vais utiliser ces données".

                  Et, du coup, tu te retrouve à utiliser le polymorphisme paramétrique : l'adaptation du comportement (qui fera toujours la même chose) permet d'utiliser ta classe avec différents types de données, tant que les types de données utilisés respectent certains prérequis, certains concept (le fait d'être multipliables entre eux, dans le cas présent).

                  Cette sorte de polymorphisme n'a absolument rien à voir avec les fonctions virtuelles, car ponderate_sum<int, int, int> représente -- par nature -- un type différente de ponderate_sum<float, float, float>, bien que tu puisse utiliser ces deux types de données de manière strictement identiques.

                  Cela n'a donc absolument rien à voir avec le LSP (Liskov Substitution Principle, ou Principe de Substitution de Liskov) qui donne "un cadre légal" à l'utilisation du polymorphisme par inclusion.

                  Pire encore, ta classe ponderate_sum n'a asolument aucune raison d'intervenir dans une hiérarchie de classes.

                  Tout comme il n'y a absolument aucune raison de faire hériter une classe Point3D de la classe Point2D parce qu'il s'agit de données qui évoluent dans des référentiels clairement distincts, si tu en venais à souhaiter prévoir plusieurs "méthodes de calcul" pour ta somme pondérée, tu ne voudrais sans doute jamais mélanger les sommes obtenues par les différentes méthodes au sein d'une collection unique:

                  tu placerais sans doute toutes les sommes qui utilisent la méthode A ensemble et toutes les sommes qui utilisent la méthode B ensemble, mais tu n'aurait aucun intérêt à mélanger deux sommes qui utilisent la méthode A avec trois sommes qui utilisent la méthode B et cinq sommes qui utilisent la méthode C.

                  Et, de la même manière, tu ne voudrais sans doute pas mélanger deux sommes pondérées qui manipulent des entier (int) avec trois sommes qui manipules des réels (float) ;)

                  Ton destructeur n'a donc absolument aucune raison d'être virtuel dans le cas présent ;)

                  (quant à la possibilité de mettre en place différentes méthodes de calcul, elle nécessitera la mise en place de différentes "politiques de calcul" qui seront représentée par un paramètre template particulier ;) )

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                    17 août 2018 à 17:06:07

                    Merci encore pour tes réponses,

                    elles m expliquent  ce que j ai demandé et le plus elles m expliquent également pourquoi je n ai pas compris ma question :p

                    Et du coup j en profite pour chercher a résoudre le problème du jour(qui est un problème de débutant...je précise parceque l autre jour des gens ont essayé de répondre a une de mes questions avec des concepts disons assez éllaborés(enfin probablement vu qu il faudrait que j ai tout compris pour en témoigner ).

                    le probleme du jour rebondit sur ce que tu as dit :

                    "

                    Ainsi, si tu crées une hiérarchie de classes proche de

                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    class Base{
                    /* ... */
                    };
                    class Derivee1 : public Base{
                    /* ... */
                    };
                    class Derivee2 : public Base{
                    /* ... */
                    };
                    /* ... */
                    class DeriveeN : public Base{
                    /* ... */
                    };

                    C'est très certainement parce que tu prévois d'utiliser des instances de Derivee1, Derivee2 ... DeriveeN.  Et donc le destructeur de la classe Base ne devra être public et virtuel que

                    • si tu veux pouvoir créer des instances de Base ET
                    • si tu  prévois de créer des collections de pointeurs sur des instances de Bases qui pointeront en réalité sur des instances de Derivee1, Derivee2 ... DeriveeN."

                    Je suis dans une situation de ce genre: en gros j ai fait un tableau d element de pointeur de la classe Base

                    et a l instanciation des case de mon tableau j ai fait des new (Derivée N).

                    Ceci dans l espoir...de pouvoir utilisé les particularité de mes dérivées N. du coup ma question est :

                    Le seul moyen d acceder a ces méthodes de ma dérivée N c est le polymorphisme?

                    pour reprendre un exemple du cour, si dans ma classe moto(qui dérive de véhicule) j ai une fonction graissage_de_guidon qui ne correspond donc pas a une methode  de la classe de base, est ce que si je receuille le pointeur de la moto(tout en m assurant que que c est bien une moto en posant une clé string"type_de_vehicul" au besoin)dans le tableau de pointeur de vehicul je vais pouvoir me servir de graissage_de_guidon?

                    a priori quand j ai instancié avec new le pointeur a l air de comporter une indication de ce sur quoi il pointe?

                    -
                    Edité par GouyFred 17 août 2018 à 17:25:59

                    • Partager sur Facebook
                    • Partager sur Twitter

                    erreur de compile sur vector

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown