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 :

Le mot clef auto


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite Avatar de ctxnop
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2007
    Messages
    858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Morbihan (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2007
    Messages : 858
    Par défaut Le mot clef auto
    Bonjour,
    Il semble y avoir un sorte de consensus global (beaucoup d'experts de beaucoup de langages différents) sur le fait qu'il serait préférable d'utiliser le mot clef "auto" (ou équivalent dans les autres langages) plutôt que de typer directement "à l'ancienne".
    Je comprend tout à fait l'utilité dans certains cas, notamment quand les noms de types deviennent extrêmement longs à cause des templates par exemple.
    Cependant je reste frileux sur le sujet. J'ai la conviction que l'utilisation systématique de cette fonctionnalité entraine des situations où un refactoring changera la sémantique sans que ça ne provoque d'erreur de compilation. Et cette situation me déplait fortement.
    Exemple :
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    int getLastDocumentCode() { return 12; }
     
    void saveDocument()
    {
        auto lastDocumentCode = getLastDocumentCode();
        lastDocumentCode = lastDocumentCode + 1;
        auto documentId = "DC"s + lastDocumentCode;
    }

    Dans ce code on s'attend à ce que documentId soit égal à "DC13". Mais si jamais on fait un refactoring pour avoir std::string getLastDocumentCode() { return "12"s; }, alors documentId sera égal à "DC121".
    La sémantique à changée et pourtant le compilo n'a rien dit. Alors évidement, le code est loin d'être parfait, mais ce n'est qu'un exemple. Et puis de toute façon, dans la vrai vie, les codes qu'on se traines depuis des années sont très très loin d'être parfaits...
    En attendant, si lastDocumentCode avait été typé "int", le changement d'interface aurait provoqué une erreur de compilation, ce qui me satisfait plus que de compter sur l'exhaustivité des tests pour éviter que le problème ne soit découvert en prod...

    Ai-je raison de penser ça ? Ou l'exemple donné (que je n'ai pas testé) est trop bidon et ne peut se produire réellement ? Peut-on vraiment mettre auto partout ?
    D'un point de vue plus personnel je trouve que ça nuit à la lisibilité de ne plus voir les types, être obligé d'avoir un IDE et de mettre son curseur sur la variable pour savoir ce qu'a "deviné" le compilo...

  2. #2
    Membre éprouvé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Par défaut
    Lu'!

    La tendance tire vers almost always auto.

    Il y a une partie à ne pas négliger c'est qu'une part de ton travail est d'assurer que le type que tu vas manipuler correspond à ce que tu attends. Déjà en ce qui me concerne, je n'aime pas vraiment l'exemple parce que typiquement un code, c'est un truc qui est sensé avoir un format, ce qui à mon sens justifie d'avoir un type spécialisé. L'autre raison pour laquelle je n'aime pas trop cet exemple, c'est qu'il ne pointe pas un problème avec auto mais un problème avec std::string. Le symbole "+" pour la concaténation, c'était pas franchement l'idée du siècle (et oui, plein de langages le font, et non, c'est pas une raison pour faire pareil), en même temps on ne peut pas définir n'importe quoi comme étant une notation en C++ donc c'était le mieux qu'on puisse faire.

    Pour le auto dans les blocs, j'ai tendance à dire :
    • Inférence dans la majorité des cas
    • typage manuel quand on a besoin d'un type précis pour respecter son contrat
    • typage manuel pour la création d'une variable sans dépendance


    Pour le auto dans les paramètres/retour de fonction :
    • quand la sémantique de la fonction est très claire
    • et que le contrat des types qu'elle reçoit/retourne est évident


    Citation Envoyé par ctxnop Voir le message
    D'un point de vue plus personnel je trouve que ça nuit à la lisibilité de ne plus voir les types, être obligé d'avoir un IDE et de mettre son curseur sur la variable pour savoir ce qu'a "deviné" le compilo...
    Normalement, une bonne fonction, c'est une fonction courte avec un accès restreint au scope, ce qui veut dire que la déclaration des éléments qu'on utilise ne doit globalement pas se trouver bien loin : soit c'est un paramètre, soit ça fait parti de l'objet qu'on manipule (donc on a son header ouvert pas trop loin), soit c'est un retour de fonction dont on connaît le type de retour. L'autre chose c'est qu'un code clair utilise des noms clairs, donc a priori le nom de la variable doit nous en dire autant, sinon plus, à son sujet que son type.

  3. #3
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    Dans l'absolu, ton code d'exemple n'est pas faux.

    Mais normalement, il ne faudrait pas faire comme cela (a mon avis). Tu as un document qui possède un code et qui être sauvegardé. Donc il faudrait avoir une classe document qui possède une fonction save.
    L'autre problème est que retourner un code est équivalent a avoir un getter et donc (petite) violation de Demeter.
    Au final, le problème est que tu as un code qui dépend du type de "code", mais qui est séparé dans 2 endroits différents (perte de localité de l'information). Cela implique que celui qui modifie le code de "document" doit vérifier aussi que le code client utilise correctement la classe "document", ce qui ne devrait pas avoir lieu. Si ces 2 informations sont co-localisées dans la même classe, celui qui modifie la classe document ne doit vérifier que la cohérence de ses changements dans la classe "document", pas dans le code client.

    Bref, c'est pas faux dans le sens ou l'on voit souvent ce type de code dans un programme réel, mais c'est un problème de conception a la base (qui a pour conséquence de diminuer la robustesse du code = le code est sensible aux changements, comme on peut le voir dans ton exemple)

  4. #4
    Membre émérite Avatar de ctxnop
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2007
    Messages
    858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Morbihan (Bretagne)

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

    Informations forums :
    Inscription : Juillet 2007
    Messages : 858
    Par défaut
    Tout d'abord, merci de vos participations.

    Cependant je pense que vous vous attachez trop à l'exemple.
    J'ai pris l'exemple d'un string parce que c'est standard et donc c'est déjà ce qui est fait à beaucoup d'endroits. Mais le problème reste entier avec tout le reste des codes. J'ai utilisé l'opérateur + ici, mais on peut tout à fait imaginer une méthode classique qui poserait le même problème. Après tout les opérateurs ne sont que des méthodes comme les autres. Je suis persuadé qu'on peut faire un exemple qui respecte tous les préceptes d'un code de qualité et qui pourtant provoque le problème.

    Je suis on ne peut plus d'accord pour dire que les règles de bonnes pratiques disent qu'on fait des fonctions qui prennent le moins de paramètres possible, qui ne doivent pas faire 500 lignes, qu'on nomme les variables correctement, etc...
    Mais croire que c'est respecté partout, tout le temps, par tout le monde, c'est juste utopique. Et ce dans quelque langage que ce soit.

    De mon point de vue, pousser à l'utilisation en toute circonstance, "les yeux fermés", à l'utilisation d'une fonctionnalité qui induit un risque dans la vie des codes existant ou des codes qui ne sont pas parfaits en terme de propreté, ce n'est pas judicieux. J'ai regardé de nombreuses conférences sur le sujet, lu beaucoup d'article ou tuto, etc... et pas une fois le problème n'est évoqué.

    Savoir que "count" est une variable "int" parce qu'elle est déclarée "auto count = 12;" je suis tout à fait d'accord qu'il n'y avait pas besoin de la typer "int" explicitement. Cependant si elle est déclarée "auto count = getCount();" comment je sais quel est son type réel au premier coup d'oeil ? Je sais que c'est très probablement un int vu le nom, mais ca pourrait être un unsigned int, un long, un short, ou je ne sais quoi d'autre. A moins d'avoir la fonction getCount() sous les yeux je ne peux pas être certain du type dans "demander" au compilo ou sans aller voir la définition de getCount(). Dans tous les cas c'est plus lourd à faire que de lire "int count = getCount();".
    On peut argumenter sur le fait que je n'ai peut être pas à le savoir, ou que c'est pas propre de faire comme ça, ou je ne sais quoi, mais la réalité c'est que ces codes font partis de nos quotidien et il faut bien faire avec.

    Puisqu'il y a des cas, relativement nombreux, où "auto" n'apporte absolument rien et/ou que le typage classique est plus lisible, pour être cohérent j'aurais tendance à toujours tout typer de la même façon, partout. Et donc j'aurais tendance à typer classiquement. Les seuls cas où je trouve qu'auto à de l'intérêt c'est lorsque le type devient trop long du genre std::vector<int>::const_iterator.

    PS: j'ai édité vu qu'une réponse s'est insérée entre la mienne et la première réponse

  5. #5
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    Oui, tu as raison pour le support du code existant. L'utilisation de auto (AAA) nécessite au préalable que le code soit robuste. Il serait dangereux de faire les choses a moitié, c'est a dire de passer tout a auto sur un code mal conçu.
    Mais ceux qui préconiseur le AAA prêche aussi pour la bonne conception, donc tout va bien.

  6. #6
    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
    Sauf que le type de bidule.getCount(), ce n'est ni int, ni short, ni long, mais Bidule::size_type.

    Un auto, c'est un type du bon type, qui sert, non pas à toi développeur, mais bien au compilateur.
    Toi, tu n'as pas besoin de savoir ce que c'est comme type.

    Ici, j'ai par exemple envie de pouvoir boucler, et j'aurai donc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for (auto i=0, end = bidule.getCount(); i!=end; ++i) {
        stream << "l'élément "<<i<<" est " << bidule(i) <<'\n';
    }
    Je veux seulement que i et end soit du même type (d'où la déclaration sur le même auto), et soit ++able.
    Ca pourrait parfaitement être un ptrdiff_t ou un iterateur magiquement convertible en unsigned short.

    Les types ne servent vraiment qu'aux paramètres et aux membres des classes.
    Et en général, je colle des typedef de partout.

    Pour deux raisons:
    la première est une considération pratique, je peux passer à une template, ou en revenir, sans grand effort.
    La seconde est sémantique: je veux que les types soit nommés selon leurs utilisations, pas leurs formes.

    Pour une date, j'aurais trois types de bases (day_type, month_type, year_type), et des types complémentaires (tels que difference_type).
    En fait, la présence d'un type primitif (hors bool) dans l'interface publique de mes fonctions me fait systématiquement réfléchir.

    Par contre, pour tout ce qui est privé (implémentation, etc), auto est largement suffisant.
    De toute façon, comme je fais en sorte de maximiser les chainages, et de limiter au maximum la taille des fonctions, je n'ai quasiment jamais de variables locales.
    Sauf des auto const& pour les boucles.

    Je peux raisonnablement dire que 90% de mes fonctions ne contiennent pas de variables locales.

    Et quand c'est le cas, ca donne des choses telles que (extrait de mémoire du démineur que je code pour m'exercer sur la SFML):
    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
    public:
        bool jouer(position_type const& p) {
            if (!valid(p) || !at(p).accepts(current()) ) return false;
            activate(p);
            return true;
        }
     
    private:
        void activate(position_type const& p) {
            auto& here = at(p);
            if (here.seen()) return;
            here.activate();
            if (here.empty()) {
                for(auto shift : neighbours()) {
                    auto there = p+shift;
                    if (valid(there)) activate(there);
                }
            }
        }
        static std::vector<shift_type> neighbours() {
            return {{-1,-1}, {-1, 0},/**/};
        }
    trois fonctions, trois temporaire, et encore, il y en a une pour éviter de calculer deux fois une somme entre une position et un décalage.
    Je n'ai pas une seule fois besoin des types réels, seulement de nommer des informations que j'utilise (position actuelle, décalage dans la grille, etc).

    Si je passe à un démineur en 3, 4 ou 17D, je n'ai que deux choses à changer: les typedef (position_type et shift_type, et neighbours).
    (par contre, pour l'interface graphique en 17D, ca va être plus galère )

  7. #7
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    (using, plus typedef )

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

Discussions similaires

  1. Intérêt du mot-clef 'auto'
    Par Invité dans le forum Langage
    Réponses: 6
    Dernier message: 11/06/2014, 14h58
  2. mot-clef static
    Par keil dans le forum C++
    Réponses: 8
    Dernier message: 25/01/2006, 17h11
  3. Variables automatiques et mot clé auto, variables register.
    Par Évariste Galois dans le forum C++
    Réponses: 6
    Dernier message: 11/08/2005, 20h30
  4. mot clef sql pour nom de champ
    Par bobinou007 dans le forum Langage SQL
    Réponses: 4
    Dernier message: 12/10/2004, 13h21

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