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

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    mai 2007
    Messages
    146
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : mai 2007
    Messages : 146
    Points : 116
    Points
    116

    Par défaut héritage de construction

    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct A { 
    A(int) {} 
    void test();
    };
    struct B : public A {};
     
    void test() {
        A a(3);
        B b(3); // error no matching constructor for B(int)
        B b();
        b.test(); // Ok  --> jalousie : pourquoi A::test est-il accessible et pas A::A()?
    }
    Quelqu'un pourrait-il m'expliquer pourquoi il est obligatoire de déclarer un constructeur B(int) (la question du gros flemmard) ?
    En effet la sémantique de l'héritage est "est un" et donc, pour un héritage public, je m'attends à ce que TOUS les membres public et protected soient accessibles directement dans ma classe fille (selon leur classe d'accès bien sur), d'où ma question : Pourquoi tant de haine?
    Ca a une importance pour moi par rapport aux template, dans la mesure où on ne peut pas spécialiser explicitement les using :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    template <typename t> using B = UneDefinition<t>
    template <> using B<test> = A<test,autres paramètres>; // Erreur spécialisation explicite d'un 'using' interdite
    // interdit, c'est pourtant ce dont j'aurais besoin
    Je me suis dit que je pourrais remplacer par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <> struct B<test> : public A<test, autres paramètres> {};
    Mais ça m'impose de redéclarer tous les constructeurs et ça me dérange (lourdeur, maintenance), d'où ma question.

    Merci par avance pour vos réponses.

  2. #2
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 820
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 820
    Points : 6 219
    Points
    6 219

    Par défaut

    inheriting constructors (<- lien stackoverflow en anglais)


    En gros, les constructeurs ne sont pas hérités en C++03 : c'est un mix entre
    • Comme il faut initialiser les nouveaux membres, le C++ oblige de [re]définir un constructeur
    • Le compilateur n'est pas sensé savoir que le constructeur B veut appeler le constructeur A (pour ton exemple)


    En C++11 on peut utiliser using

  3. #3
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 257
    Points : 10 765
    Points
    10 765

    Par défaut

    Citation Envoyé par Teaniel Voir le message
    la sémantique de l'héritage est "est un"
    C'est bien plus subtil. Les propriétés de l'héritage public font qu'il autorise une substituabilité syntaxique. Et si elle n'est pas plus riche que ça (LSP), c'est casse gueule. Très casse gueule.
    EST-UN est un faux ami. EST-SUBTITUABLE-A est bien plus exact et à préféré quand on réfléchit.

    Quant à la substituabilité du polymorphisme d'inclusion (l'héritage public), elle concerne les objets qui existent déjà. Avant la construction, un objet n'existe pas. (En plus avec les template, on change de paradigme et là... en C++ c'est ... pas le chaos... mais bon.)

    L'héritage des constructeurs est un sucre syntaxique récent (cf message de mon VDD), mais c'est juste là pour aider. Et il faut que cela soit explicite.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    mai 2007
    Messages
    146
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : mai 2007
    Messages : 146
    Points : 116
    Points
    116

    Par défaut

    Bonjour,

    Merci à tous les deux pour vos réponses.
    Pour "m'aider" j'utilise toujours une image : dans la nature, les oiseaux volent. Donc un pigeon vole. Ok. Un pigeon est un oiseau. Personne ne dira le contraire.
    Pour créer un arbre, on plante (ou sème) une graine. On sait donc que la construction d'un chêne se fera ainsi. C'est comme cela que je sens l'héritage.

    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
    template <typename _T> struct A {
    A() {}
    A(int) : ... {...}
    // N autres constructeurs
    f() {}
    };
     
    template<> struct A<un_type> {
    A(float) : ... { ... }
    // P autres constructeurs
    f() {}
    };
     
    template <typename _T> struct B : public A<_T>{};
     
    void f() {
        A<un_type> a(3.5); // Ok
        B<un_type> b(3.5); // impossible
     
    }
    Dans ce cas, je suis donc obligé de reproduire les N lignes du constructeur de A dans B, puis d'ajouter une spécialisation à B pour reproduire les P constructeurs de A<un_type>, N + P constructeurs qui ne feront RIEN hormis d'appeler celui du parent... Je trouve que ça fait potentiellement beaucoup de travail pour ce RIEN...
    Je croyais que C++ était un langage concis???
    Pour moi, B<_T> est un A<_T> y compris dans sa manière d'être créé. C'est d'ailleurs si vrai que c'est bien la fonction f de A qui sera appelée si on écrit b.f();, et pas une reproduction. Pourquoi ne pourrait-on pas appeler le constructeur correspondant du parent s'il n'est pas trouvé dans l'enfant ? Ca retirerait N+P lignes de mo code, aidant ainsi à le rendre clair et compréhensible.

    C'est pourquoi vos réponses ne me satisfont pas... Désolé.

    Merci encore pour vos éclairages
    @++

  5. #5
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 257
    Points : 10 765
    Points
    10 765

    Par défaut

    Le C++ est tout sauf un langage concis.

    Quid si on change l'invariant du fils et qu'il ne faut surtout pas laisser les constructeurs parents?
    Ce nouvel invariant peut être la conséquence d'un nouveau membre ou d'un sous ensemble de valeurs acceptables pour la spécialisation où on veut impérativement forcer une valeur sur la constructeur parent.

    Hériter les constructeurs par défaut, n'est pas forcément le bon choix.

    Bref, qu'est-ce qui ne te convient pas dans l'héritage de constructeurs du C++11 que mon VDD a évoqué?

    PS: la nature se modélise très mal en OO rien que les manchots font que c'est une véritable galère. Cherche "Principe de Substitution de Liskov", on a eu des fils très riches sur le sujet il y a des années. Lis les bien.
    On ne peut pas raisonner en termes de EST-UN. Seul EST-SUBSTITUABLE-A marche en toutes circonstances, et cela s'applique aux objets, pas aux types, sauf éventuellement dans un monde template moyennant un peu de meta-prog éventuellement
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    mai 2007
    Messages
    146
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : mai 2007
    Messages : 146
    Points : 116
    Points
    116

    Par défaut

    Bonjour,

    Merci pour tes informations.
    J'ai (re à ma grande honte) lu des billets sur Liskov. Et donc ok, je me suis mal exprimé sur le 'Est un'. Merci aux anglais, qui utilisent l'expression 'Is a'...

    @foetus Désolé la dernière ligne de ta réponse m'a échappé concernant using : dans mon cas ce n'est pas possible, car cela spécialiserait un template using (mon exemple est une simplification de mon problème réel).

    Quid si on change l'invariant du fils et qu'il ne faut surtout pas laisser les constructeurs parents?
    Je dirais : surcharge. Les constructeurs ne pouvant pas être virtuels...

    Hériter les constructeurs par défaut, n'est pas forcément le bon choix.
    Je suis d'accord, mais cela peut aussi l'être. Et je parie que ça le serait beaucoup plus souvent.

    Pardonne moi mon ignorance, mais je ne sais pas ce qu'est ton VDD. C'est sans doute évident, mais moi et l'évidence...
    @++

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    février 2005
    Messages
    4 708
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : février 2005
    Messages : 4 708
    Points : 11 082
    Points
    11 082

    Par défaut

    Voisin Du Dessus : celui (ou ceux) qui a (ont) répondu(s) avant.

  8. #8
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 257
    Points : 10 765
    Points
    10 765

    Par défaut

    Pour IS-A, même chez eux c'est un faux ami, et on a vu la distintion entre est-substituable-à et est-implenté-en-termes-de -- je ne sais plus si c'était dans un GOTW/(M)XC++ d'Herb Sutter ou ailleurs.

    Pour using, je ne vois pas où est le problème.

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct M : private noncopyable {
        M(int);
        M(float);
        virtual ~M();
    };
     
    struct F : M {
        using M::M; // importe tous les constructeurs
    };
    Rajoute autant de template que tu veux, cela ne fera aucune différence. C'est exactement ce que tu veux: importer et ré-exposer tous les constructeurs parents sans distinction aucune.

    Et si ça devenait le défaut et qu'il fallait utiliser une surcharge masquante avec `=delete` pour supprimer un constructeur importé et ré-exposé, franchement, ça serait bien complexe. Les règles de génération des fonctions spéciales sont déjà assez compliquées AMA. Et du coup, les chances qu'ils changent ce vieux choix sont très faibles AMA toujours.

    J'ai trouvé plus intéressant les discussions autour de l'au-génération des opérateurs == et !=. Et s'il y a des règles que je prendrais par défaut, ce sont celles que j'exige dans mes bases de code: s'il y a un destructeur virtuel, copie et affectation doivent être inhibées -- sauf que je suis sûr qu'il existe beaucoup de code qui font ce mauvais choix de slicing ou qui virtualisent les destructeurs systématiquement sans avoir jamais réfléchi à la question.

    Bref. Un choix de cet acabit, bon ou mauvais, ne se change pas comme ça.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    mai 2007
    Messages
    146
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : mai 2007
    Messages : 146
    Points : 116
    Points
    116

    Par défaut

    Re bonjour,

    Oups mes excuses, tu viens de me donner la réponse que j'attendais. En réalité ma question n'a pas de sens. Je n'avais simplement pas pensé à la diffusion par using.
    Cela dit je crois qu'il n'aurait pas été gênant de diffuser automatiquement tout constructeur public ou protégé dans sa visibilité (à l'instar des fonctions membres).
    On n'est jamais obligé d'utiliser une fonctionnalité existante. Ca peut être bien plus compliqué de la créer.

    Merci pour l'intérêt porté à mon sujet.
    Et désolé, je n'ai pas un grand art pour bien poser les questions.

    @++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème héritage et construction de classe
    Par BadThing dans le forum Langage
    Réponses: 18
    Dernier message: 19/08/2010, 17h15
  2. [Postgresql]Héritage
    Par lheureuxaurelie dans le forum PostgreSQL
    Réponses: 13
    Dernier message: 02/10/2008, 09h18
  3. [JBuilder 7] Construction d'executable natif
    Par renaudfaucon dans le forum JBuilder
    Réponses: 3
    Dernier message: 24/11/2006, 22h28
  4. [Postgres] Héritage + Clés
    Par k-reen dans le forum PostgreSQL
    Réponses: 6
    Dernier message: 21/05/2003, 16h37
  5. Héritage entre Forms
    Par BarBal dans le forum Composants VCL
    Réponses: 7
    Dernier message: 29/08/2002, 17h44

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