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 :

Member vs Non-member functions


Sujet :

Langage C++

  1. #1
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Par défaut Member vs Non-member functions
    Bonjour,

    J'ai récemment lu cet article et j'ai été surpris par la solution conseillée :

    Je comprend l'argument pour mettre les fonctions qui n'ont pas besoin d'accéder aux données privées comme non-membre mais je ne partage pas cet avis dans la plupart des cas.

    J'ai l'impression que les avantages de mettre ces fonctions comme étant membre (hors opérateurs binaires) sont :

    1. Si jamais on doit optimiser cette fonction par la suite et accéder aux membres privés, il n'y a rien à changer ni à rajouter friend
    2. Facilité d'utilisation pour les templates
    3. Uniformité de la syntaxe
    4. Ressemblance aux autres langages
    5. Évite d’encombrer les espaces de noms


    Et les désavantages cités sont :

    1. Moins d'encapsulation
    2. Temps de compilation plus longs


    Maintenant, regardons les contre arguments donnés pour les avantages :

    1) Pas de contre argument donné, car possibilité non citée dans l'article (uniquement le cas où on optimise déjà la fonction est évoqué, pas une optimisation future imprévue). On peut toujours utiliser friend cependant.
    2) Pas de contre arguments, c'est justement un des cas légitimes selon l'article.
    3) Ici l'article dit que la syntaxe ne sera de toute manière pas uniforme : si on a pas accès (en modification) à la classe, on est obligé de rajouter les fonctions comme non-membre (pourquoi ne pas hériter ?)
    4) et 5) sont des avantages négligeables

    Regardons maintenant les contre arguments pour les désavantages :

    1) Et si ces fonctions qu'on implémente comme étant membre n'utilisent que l'interface public de la classe ? Il n'y a donc pas besoin de modifier ces fonctions lors d'un changement interne à la classe...
    2) Pas de contre argument, il est vrai que tout mettre dans la classe empêche de dissocier les headers.

    Ce qui nous laisse avec :

    Pour : Optimisation future sans utilisation de friend, templates, uniformité avec un ajout dans la hiérarchie de classe mais sans rajouter de fonctions virtuelles
    Contre : Temps de compilation

    Je ne suis donc pas hyper convaincu, pourtant l'auteur en connais beaucoup plus que moi sur le C++ et doit avoir de bonnes raisons. Ais-je oublié quelque chose, y a t-il quelque chose qui est beaucoup plus important qu'il n'y parait ?

    De plus, il me semble que la bibliothèque standard propose toutes ses fonctions associées à une classe (les fonctions génériques ne sont pas associées à une classe) comme membre : par exemple, on aurait pu implémenter vector::empty à partir de l'interface publique (vector::size()==0) sans dégrader les performances. Pourtant, empty est membre (bon peut être par uniformité par rapport à list et ainsi permettre l'utilisation de template...). En tout cas, je ne vois pas d'exemple de fonction liée à une classe, non membre, non opérateur binaire. Si vous en avez un, dites-le moi^^

    Merci,
    NoIdea

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    En fait, dans bien des cas, la STL n'est vraiment pas l'exemple à suivre pour une raison bien simple : elle a été implémentée avec l'idée -- tout à fait farfelue au vu des possibilités du C++ -- que tout était objet (en gros, et en caricaturant à peine, dans une idée très "javaiste" du développement).

    Le problème, c'est que les mentalités ont énormément évolués depuis lors, mais que, comme la STL fait partie de la norme, et qu'il y a donc énormément de lignes de code qui l'utilisent comme telle, il est maintenant purement et simplement impossible de faire marche arrière sans "tout casser", et donc sans abandonner l'idée d'une compatibilité ascendante accrue.

    Cependant, l'auteur de ce ticket a tout à fait raison : si tu pars d'une feuille blanche, tu as tout intérêt à utiliser autant que faire se peut les fonctions libres. La principale raison est que toute fonction (qu'il s'agisse -- forcément -- des fonctions membre (publiques) ou de fonctions libres) qui fait référence ou qui utilise une classe doit être considérée comme faisant partie de l'interface publique de la classe en question.

    Ainsi, si tu as une fonction membre d'une classe D qui nécessite un paramètre dont le type est une classe C, on peut considérer que cette fonction membre fait réellement partie de l'interface publique de C (en plus de faire partie de l'interface de D, bien évidemment ).

    Une fois que tu as cette notion "d'interface étendue" en tête, tu te rend compte que l'interface publique d'une fonction finit par être très (mais alors, vraiment très) importante, tend à en compliquer singulièrement l'usage, surtout si toutes les fonctionnalités qui font partie de cette interface publique sont effectivement accessibles depuis un seul et même fichier d'en-tête (comme ce serait le cas si toutes ces fonctions étaient définies sous la forme de fonctions membres).

    Et, bien sur, si l'on déclare toutes ces fonctions dans un seul fichier d'en-tête, les temps de compilation vont littéralement exploser : à chaque fois que les gardes anti inclusion n'auront pas supprimé le contenu du fichier d'en-tête, le compilateur devra forcément lire (et manipuler) le contenu du fichier en entier, ce qui est dommage car, ce faisant, il va peut être "perdre son temps" à définir les symboles correspondant à un grand nombre de fonctions dont nous n'avons absolument pas l'usage dans une situation donnée.

    Du coup, l'idée (qui sert d'ailleurs de base à l'ISP) est de partir du point de vue que, si l'on n'a pas une excellente raison de créer une fonction membre, nous avons sans doute intérêt à créer une fonction libre et, tant qu'à faire, à la déclarer dans un fichier d'en-tête séparé. Cette manière de travailler va permettre plusieurs choses :

    D'abord, tes fichiers d'en-tête deviennent beaucoup plus "courts". Cela facilite le travail qui consiste à se créer une "carte mentale" des fonctionnalités auxquelles ce fichier donne accès (il n'y a rien à faire, on lira beaucoup plus facilement un fichier composé d'une petite centaines de lignes qu'un fichier composés de plusieurs milliers de lignes ).

    De plus, cela permettra au compilateur de n'avoir à créer les symboles que des fonctionnalités dont on a réellement besoin : si tu as besoin d'une fonctionnalité, il est possible que tu aies aussi besoin des fonctionnalités "qui vont bien" avec, mais si tu n'en as pas besoin, pourquoi devrait-il perdre son temps à créer les symboles en question .

    Enfin,les gardes anti inclusion multiples et les déclarations anticipées permettront de faire en sorte de limiter très fortement la taille totale des informations à traiter

    Et puis, cerise sur le gâteau, il reste toujours la possibilité de déclarer une fonction libre comme étant amie d'une classe pour éviter d'avoir à exposer des détails qui seraient de nature à briser l'encapsulation.

    Bref, sous réserve de travailler correctement (comprends: d'utiliser autant que possible les déclarations anticipées et / ou l'amitié quand c'est réellement nécessaire), tu as sans doute "tout à gagner" à ne pas avoir une approche strictement basée sur le paradigme orienté objets. C'est d'autant plus vrai que le langage te permet de le faire, dés lors, pourquoi voudrais tu t'en priver
    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

  3. #3
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    En fait, dans bien des cas, la STL n'est vraiment pas l'exemple à suivre pour une raison bien simple : elle a été implémentée avec l'idée -- tout à fait farfelue au vu des possibilités du C++ -- que tout était objet (en gros, et en caricaturant à peine, dans une idée très "javaiste" du développement).
    Il est probable que si la STL devait être refaite, il y aurait des changements. Cependant, celle-ci continue de très bien fonctionner et il me semble que tous ces composants s'interfacent bien. As-tu un exemple précis en tête (en relation avec la question des fonctions membres) ?

    Le problème, c'est que les mentalités ont énormément évolués depuis lors, mais que, comme la STL fait partie de la norme, et qu'il y a donc énormément de lignes de code qui l'utilisent comme telle, il est maintenant purement et simplement impossible de faire marche arrière sans "tout casser", et donc sans abandonner l'idée d'une compatibilité ascendante accrue.

    Cependant, l'auteur de ce ticket a tout à fait raison : si tu pars d'une feuille blanche, tu as tout intérêt à utiliser autant que faire se peut les fonctions libres. La principale raison est que toute fonction (qu'il s'agisse -- forcément -- des fonctions membre (publiques) ou de fonctions libres) qui fait référence ou qui utilise une classe doit être considérée comme faisant partie de l'interface publique de la classe en question.
    Oui, mais dans ce cas, l'interface est tellement étendue qu'elle n'a plus de sens. Regarde juste l'interface que ça donne pour les itérateurs...

    Ainsi, si tu as une fonction membre d'une classe D qui nécessite un paramètre dont le type est une classe C, on peut considérer que cette fonction membre fait réellement partie de l'interface publique de C (en plus de faire partie de l'interface de D, bien évidemment ).

    Une fois que tu as cette notion "d'interface étendue" en tête, tu te rend compte que l'interface publique d'une fonction finit par être très (mais alors, vraiment très) importante, tend à en compliquer singulièrement l'usage, surtout si toutes les fonctionnalités qui font partie de cette interface publique sont effectivement accessibles depuis un seul et même fichier d'en-tête (comme ce serait le cas si toutes ces fonctions étaient définies sous la forme de fonctions membres).

    Et, bien sur, si l'on déclare toutes ces fonctions dans un seul fichier d'en-tête, les temps de compilation vont littéralement exploser : à chaque fois que les gardes anti inclusion n'auront pas supprimé le contenu du fichier d'en-tête, le compilateur devra forcément lire (et manipuler) le contenu du fichier en entier, ce qui est dommage car, ce faisant, il va peut être "perdre son temps" à définir les symboles correspondant à un grand nombre de fonctions dont nous n'avons absolument pas l'usage dans une situation donnée.

    Du coup, l'idée (qui sert d'ailleurs de base à l'ISP) est de partir du point de vue que, si l'on n'a pas une excellente raison de créer une fonction membre, nous avons sans doute intérêt à créer une fonction libre et, tant qu'à faire, à la déclarer dans un fichier d'en-tête séparé. Cette manière de travailler va permettre plusieurs choses :

    D'abord, tes fichiers d'en-tête deviennent beaucoup plus "courts". Cela facilite le travail qui consiste à se créer une "carte mentale" des fonctionnalités auxquelles ce fichier donne accès (il n'y a rien à faire, on lira beaucoup plus facilement un fichier composé d'une petite centaines de lignes qu'un fichier composés de plusieurs milliers de lignes ).

    De plus, cela permettra au compilateur de n'avoir à créer les symboles que des fonctionnalités dont on a réellement besoin : si tu as besoin d'une fonctionnalité, il est possible que tu aies aussi besoin des fonctionnalités "qui vont bien" avec, mais si tu n'en as pas besoin, pourquoi devrait-il perdre son temps à créer les symboles en question .

    Enfin,les gardes anti inclusion multiples et les déclarations anticipées permettront de faire en sorte de limiter très fortement la taille totale des informations à traiter

    Et puis, cerise sur le gâteau, il reste toujours la possibilité de déclarer une fonction libre comme étant amie d'une classe pour éviter d'avoir à exposer des détails qui seraient de nature à briser l'encapsulation.

    Bref, sous réserve de travailler correctement (comprends: d'utiliser autant que possible les déclarations anticipées et / ou l'amitié quand c'est réellement nécessaire), tu as sans doute "tout à gagner" à ne pas avoir une approche strictement basée sur le paradigme orienté objets. C'est d'autant plus vrai que le langage te permet de le faire, dés lors, pourquoi voudrais tu t'en priver
    Dans ce cas, pourquoi ne pas juste garder les constructeurs/operateur=/destructor/fonctions virtuelles dans les classes et ne pas mettre toutes les autres fonctions comme externes, quitte à utiliser friend ?

    Remarque : dans l'argumentation que je mène, je ne suis pas du tout en train de dire qu'il faut déclarer toutes les fonctions comme membre, je dis juste que j'ai l'impression que l'argumentation de l'article (et encore plus la tienne) pousse à ne pas mettre certaines fonctions qui pourraient être membre en externe alors que cela ne vaut pas le coup.

  4. #4
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Comme exemple, il y a begin et end, qui "deviennent" std::begin et std::end.
    Il y a aussi toute l'ancienne version de <functional>

  5. #5
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    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 287
    Par défaut
    Lis le Notes on Programming de Stepanov. Tu verras que si les fonctions size, empty, begin, end, ... sont membres, c'était surtout pour ne pas s'aliéner des Ayatollah de l'OO qui ne pouvaient pas comprendre que libre ou membre (Il y a une part d'interprétation de ma part que l'on reconnaitra à l'emploi de termes qui impliquent des jugements), c'est juste du sucre syntaxique. D'ailleurs le C++11 a corrigé le tir. Et c'est hyper important, c'est grâce à ça (avec le Koenig Namespace Lookup) que les based range for loops marchent. Et non, la STL est tout sauf OO dans l'esprit. Je ne peux que renvoyer aux nombreuses interventions de son auteur qui se défend de tout OOisme.

    Cf aussi le C++17 qui va encore plus loin dans le OSEF si la fonction est membre ou libre pour l'appel -> http://www.open-std.org/JTC1/SC22/WG...2015/n4474.pdf
    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 éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Citation Envoyé par leternel Voir le message
    Comme exemple, il y a begin et end, qui "deviennent" std::begin et std::end.
    Pour moi, l'avantage est surtout de pouvoir traiter des tableaux, et pas seulement des conteneurs STL.

    Ce qui me gêne, dans cet article de Meyers, c'est la définition qu'il donne du terme "encapsulation".
    Originellement, le concept d'encapsulation a trait au concept des boîtes noires : on n'a pas connaissance des données stockées dans l'objet.

    Meyers ajoute à cette notion celle des dépendances, qui pour moi est un autre problème. Cela perturbe inutilement le lecteur.
    koala01 avec son concept d'interface étendue tombe dans le même travers.

    Le parti de Meyers et des autres lecteurs de ce fil, consistant à promouvoir les fonctions libres, est légitime. C'est vrai qu'il réduit les dépendances, ce qui apporte pas mal d'avantages.
    Mais il y aussi des inconvénients : je trouve la "carte mentale", telle qu'évoquée par koala01, beaucoup plus lisible dans le cas de la POO. Il est beaucoup plus facile de faire une doc sur des classes qu'avec des fonctions libres, car on doit introduite la notion de concepts (les arguments doivent supporter tels types d'opération...), et au final, on ne sait pas trop avec quoi on peut appeler ces fonctions libres.

    Enfin, si on veut un vrai gain à la compilation, il faut effectivement atomiser les fichiers (comme avec le principe SRP), et ça devient parfois n'importe quoi.

  7. #7
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 392
    Par défaut
    Euh... J'ai du mal à comprendre comment std::begin et std::end peuvent faire autre chose que wrapper (conteneur)::begin et (conteneur)::end. Pour moi, ces fonctions ont justement besoin d'accéder aux membres privés de la classe, qu'elles passent au constructeur de l'itérateur!

    À quel endroit me suis-je trompé?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    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 287
    Par défaut
    Par défaut, ils doivent être implémentés de sorte que
    - pour des tableaux statiques ils font ce qui va bien
    - pour des types pour lesquels on détecte un membre de même nom, ils renvoient la valeur renvoyée par ce membre

    Au delà du C++17, je ne serai pas surpris que la seconde version soit retirée vu que x.f() et f(x) vont devenir automatiquement interchangeables -- et qu'ils préfèreront garder la rétro-compatibilité sur les codes qui feront une référence explicite à begin/end comme fonctions membres.

    Sinon, ils aurait pu être définis comme des fonctions libres.
    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
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    Il est probable que si la STL devait être refaite, il y aurait des changements. Cependant, celle-ci continue de très bien fonctionner et il me semble que tous ces composants s'interfacent bien. As-tu un exemple précis en tête (en relation avec la question des fonctions membres) ?
    à part les 256 fonctions membres (en différentes surcharge) de la classe std::string non pas vraiment

    Dans ce cas, pourquoi ne pas juste garder les constructeurs/operateur=/destructor/fonctions virtuelles dans les classes et ne pas mettre toutes les autres fonctions comme externes, quitte à utiliser friend ?
    Parce qu'il y a aussi des comportements pour lesquels tu voudra profiter du polymorphisme que le LSP autorise.

    Dans ce cas particulier, tu auras sans doute besoin de fonctions membres virtuelles

    Mais oui, en dehors des big four (big six) et des fonctions virtuelles, la plupart des fonctions membres pourraient être définies commes des fonctions libres et déclarées friend

    Citation Envoyé par Luc Hermitte Voir le message
    Lis le Notes on Programming de Stepanov. Tu verras que si les fonctions size, empty, begin, end, ... sont membres, c'était surtout pour ne pas s'aliéner des Ayatollah de l'OO qui ne pouvaient pas comprendre que libre ou membre (Il y a une part d'interprétation de ma part que l'on reconnaitra à l'emploi de termes qui impliquent des jugements)
    Je ne sais pas si cette remarque m'était adressée, mais, si c'est le cas, je n'ai pas du tout émis de jugement sur ce fait. Je me suis contenté de constater que l'implémentation de nombreuses fonctionnalités avaient été envisagée d'une manière excessivement OOiste

    Et, oui, je sais que cela s'est fait au grand dam de Stepanoph (car mon avis sur la question ne vaut pas vraiment tripette ). Mais je crois d'ailleurs que c'est lui-même (ou est-ce Meyers ) qui a attiré mon attention sur es 250 fonctions membres de std::string à la lecture d'un de ses articles .
    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

  10. #10
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    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 287
    Par défaut
    @Koala01,
    Non. Cette partie ne t'était pas du tout adressée. Elle l'était à l'OP.
    Le jugement de valeur est mien après.

    Pour toi, j'avais juste que Stepanov se défend d'un design d'OO --
    ; http://www.stepanovpapers.com/notes.pdf (p.21). Il s'est senti (ou a été ?) contraint à une syntaxe OO-isante pour brosser dans le sens du poil ceux qui avaient été emportés par le hype autour du OO à l'époque -- depuis, ils ont migré vers d'autres langages. Et cela s'arrête à ça. Il n'y a même pas de setters (hormis les opérateurs [], dont c'est au fond le service).

    Pour l'abus de fonctions dans std::string, qui rappelons-le n'est pas une classe de la STL, c'est Sutter qui m'avait initié au problème via les GOTW qui devinrent (M)XC++.
    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...

  11. #11
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Par défaut
    Comme exemple, il y a begin et end, qui "deviennent" std::begin et std::end.
    Euh... J'ai du mal à comprendre comment std::begin et std::end peuvent faire autre chose que wrapper (conteneur)::begin et (conteneur)::end. Pour moi, ces fonctions ont justement besoin d'accéder aux membres privés de la classe, qu'elles passent au constructeur de l'itérateur!
    Pour moi, l'avantage est surtout de pouvoir traiter des tableaux, et pas seulement des conteneurs STL.

    J'ai justement l'impression que std::begin (et std::end) ne rentrent pas dans les "fonctions liées à une classe" : elles se veulent génériques (tout comme std::find, std::sort, ...), pour toute classe ayant la notion d'ensemble ordonné. Donc il est normal qu'elles ne soient pas dans l'interface publique. Par contre, maclasse::begin() est elle liée à la classe (elle ne marche que pour la classe en question)... Après, je suis d'accord que ces deux écritures permettent la même chose. Cependant, êtes vous en train de dire, qu'il vaudrait mieux ne pas avoir de fonction de la forme maclasse::begin et juste définir des fonctions begin qui seraient friend ?
    Lis le Notes on Programming de Stepanov. Tu verras que si les fonctions size, empty, begin, end, ... sont membres, c'était surtout pour ne pas s'aliéner des Ayatollah de l'OO qui ne pouvaient pas comprendre que libre ou membre (Il y a une part d'interprétation de ma part que l'on reconnaitra à l'emploi de termes qui impliquent des jugements), c'est juste du sucre syntaxique.
    Je suis d'accord que tant qu'on utilise pas de fonctions virtuelles, l'OO n'est que du sucre syntaxique (sauf pour le RAII, qui est un sucre syntaxique extrêmement pratique). Cependant, j'ai quand même l'impression que c'est du sucre syntaxique qui organise plutôt bien le code...

    à part les 256 fonctions membres (en différentes surcharge) de la classe std::string non pas vraiment
    Oui, mais le nombre de fonctions membres de std::string est surtout du au nombre de redondances non (par exemple length et size) ? Et si les mêmes fonctions étaient fournies, mais en fonction non membre, ce serait vraiment mieux ?


    De manière générale, je n'ai pas l'impression que les fonctions membres soient vraiment le problème (dans ce que vous dites), mais que le problème vient plutôt du système archaïque de compilation... Puisque l'argument principal que vous donnez est "pour pouvoir séparer les headers".


    Ensuite, je suis d'accord que si on prend l'habitude qu'une classe ressemble à

    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
    class A
    {
        public :
            A();
            A(something);
            A(const A&);
            A(A&&);
            A& operator=(const A&);
            ~A();
        friend f1;
        friend f2;
        friend f3;
        private :
            member_1;
            member_2;
    };
     
    f1 //Accède aux membres privés
    f2 //Accède aux membres privés
    f3 //Accède aux membres privés
     
    f4 //Accède pas aux membres privés
    f5 //Accède pas aux membres privés
    ...
    Alors les désavantages 1) et 3) sautent. Mais reste le problème des templates associé aux espaces de nom : si je fais ma fonction begin externe à ma classe, mais dans le namespace maclasse, des fonctions génériques futures ne pourront pas appeler mon begin... Et on va commencer à polluer le namespace global...

    Remarque : Récemment, j'ai eu l'impression que les fonctions virtuelles pouvaient toujours être remplacées par des std::function (qui utilise virtual^^). Ainsi, il n'y aurait même plus besoin d'avoir d'autres fonctions dans l'interface publique de la classe (pour la plupart des cas en tout cas)...

    PS : Je suis en train de lire l'article/vidéo

  12. #12
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    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 287
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    a- Après, je suis d'accord que ces deux écritures permettent la même chose. Cependant, êtes vous en train de dire, qu'il vaudrait mieux ne pas avoir de fonction de la forme maclasse::begin et juste définir des fonctions begin qui seraient friend ?

    b- Je suis d'accord que tant qu'on utilise pas de fonctions virtuelles, l'OO n'est que du sucre syntaxique (sauf pour le RAII, qui est un sucre syntaxique extrêmement pratique). Cependant, j'ai quand même l'impression que c'est du sucre syntaxique qui organise plutôt bien le code...

    c- Oui, mais le nombre de fonctions membres de std::string est surtout du au nombre de redondances non (par exemple length et size) ? Et si les mêmes fonctions étaient fournies, mais en fonction non membre, ce serait vraiment mieux ?

    d- De manière générale, je n'ai pas l'impression que les fonctions membres soient vraiment le problème (dans ce que vous dites), mais que le problème vient plutôt du système archaïque de compilation... Puisque l'argument principal que vous donnez est "pour pouvoir séparer les headers".

    e- Alors les désavantages 1) et 3) sautent. Mais reste le problème des templates associé aux espaces de nom : si je fais ma fonction begin externe à ma classe, mais dans le namespace maclasse, des fonctions génériques futures ne pourront pas appeler mon begin... Et on va commencer à polluer le namespace global...
    a- C'est ce que dit Stepanov à la p.21 de son NoP

    b- Le RAII n'est pas un bonus de l'OO. Mais le bonus d'un choix de design propre au C++ qui a décidé d'introduire une notion de destructeur qui est appelé implicitement en sortie de portée.

    c- C'est l'idée. D'autant que plus d'une de ces fonctions pourrait être totalement générique et s'appliquer à des choses qui sont des séquences de caractères -- d'où boost.string_algo
    De plus, on a un problème de Cohésion : la classe offre trop de services. Côté SRP/ISP, ce n'est pas ça.
    Cf l'article de GOTW (/ le (More) eXceptional C++ qui reprend le GOTW -- http://gotw.ca/gotw)

    d-Non. Aucun rapport avec le modèle de compilation. On parle bien de conception uniquement.

    e- Que nenni. Au contraire. Les fonctions de l'interface étendues devraient être dans l'espace de nom de la classe sur laquelle elles vont s'appliquer pour pouvoir profiter du Koenig Napespace Lookup. C'est d'ailleurs pour ça que les algorithmes génériques ne vont pas utiliser "std::swap(a,b);" directement, mais faire un "using std::swap; swap(a,b);". Sur le sujet, cf les billets d'Eric Niebler sur l'emploi idéal de ces fonctions (libres ou pas) comme points de customisation: http://ericniebler.com/2014/10/21/cu...11-and-beyond/

    BTW, pour une lib (ITK) qui utilise ses propres conventions éloignées de celles du standard, j'avais pondu un proof of concept pour la rendre compatible avec les range-based for loops du C++11: http://tully.ups-tlse.fr/jordi/scrap...dIterators.org
    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...

Discussions similaires

  1. warning: control reaches end of non-void function
    Par loisir1976 dans le forum Débuter
    Réponses: 4
    Dernier message: 22/09/2009, 10h54
  2. Réponses: 3
    Dernier message: 29/01/2009, 09h43
  3. Réponses: 2
    Dernier message: 27/01/2008, 21h22

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