J'ai assisté à une conférence de Herb Sutter où il encourage tous les programmeurs C ++ à utiliser auto
.
Il y a quelque temps, j'ai dû lire le code C #, qui var
était très utilisé et très difficile à comprendre. À chaque var
utilisation, je devais vérifier le type de retour du côté droit. Parfois plus d'une fois, car j'ai oublié le type de la variable après un certain temps!
Je sais que le compilateur connaît le type et je n'ai pas à l'écrire, mais il est généralement admis que nous devrions écrire du code pour les programmeurs, pas pour les compilateurs.
Je sais aussi que c'est plus facile à écrire:
auto x = GetX();
Que:
someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();
Mais ceci n’est écrit qu’une seule fois et le GetX()
type de retour est vérifié plusieurs fois pour comprendre le type x
.
Cela m'a fait me demander - rend le auto
code C ++ plus difficile à comprendre.
auto
peut souvent rendre les choses plus difficiles à lire quand elles sont déjà difficiles à lire, c’est-à-dire des fonctions trop longues, des variables mal nommées, etc. Sur des fonctions courtes avec des variables bien nommées, sachant que les types doivent être l’un des n ° 1 facile ou n ° 2 non pertinent.auto
est un peu comme déterminer quand utilisertypedef
. C'est à vous de déterminer quand cela vous gêne et quand cela vous aidera.auto x = GetX();
, choisissez un meilleur nom quex
celui qui vous dit ce qu'il fait dans ce contexte spécifique ... c'est souvent plus utile que son type de toute façon.Réponses:
Réponse courte: Plus complètement, mon opinion actuelle
auto
est que vous devriez utiliserauto
par défaut, sauf si vous voulez explicitement une conversion. (Un peu plus précisément, "... à moins que vous ne souhaitiez explicitement vous engager dans un type, ce qui est presque toujours le cas parce que vous souhaitez une conversion.")Réponse et justification plus longues:
Ecrivez un type explicite (plutôt que
auto
) uniquement lorsque vous souhaitez réellement valider explicitement un type, ce qui signifie presque toujours que vous souhaitez obtenir explicitement une conversion en ce type. De mémoire, je me souviens de deux affaires principales:initializer_list
surprise qui enauto x = { 1 };
découleinitializer_list
. Si vous ne voulez pasinitializer_list
, dites le type - c.-à-d., Demandez explicitement une conversion.auto x = matrix1 * matrix 2 + matrix3;
capture un type d'assistance ou de proxy non destiné à être visible par le programmeur. Dans de nombreux cas, capturer ce type est normal et utile, mais parfois, si vous souhaitez réellement le réduire et effectuer le calcul, indiquez le type, c’est-à-dire, demandez explicitement à nouveau une conversion.Utilisez systématiquement
auto
par défaut sinon, car celaauto
évite les pièges et rend votre code plus correct, plus facile à maintenir et plus robuste, et plus efficace. En gros, du plus au moins important, dans l’esprit de "écris pour plus de clarté et de correction":auto
garanties, vous obtiendrez le bon type. Comme dit le proverbe, si vous vous répétez (dites le type de manière redondante), vous pouvez et vous menterez (vous vous tromperez). Voici un exemple habituel:void f( const vector<int>& v ) { for( /*…*
- à ce stade, si vous écrivez le type de l'itérateur de manière explicite, vous voulez vous rappeler d'écrireconst_iterator
(avez-vous?), Alors queauto
tout se passe bien.auto
rend votre code plus robuste face aux changements car, lorsque le type de l'expression change,auto
le problème persiste. Si vous vous engagez plutôt dans un type explicite, la modification du type de l'expression entraînera des conversions silencieuses lorsque le nouveau type sera converti en ancien type, ou des coupures de construction inutiles lorsque le nouveau type fonctionnera toujours, comme l'ancien type, mais ne sera pas converti en ancien. tapez (par exemple, lorsque vous changez unmap
en ununordered_map
, ce qui est toujours correct si vous ne vous fiez pas à l'ordre, en utilisantauto
pour vos itérateurs vous passerez de manière transparentemap<>::iterator
àunordered_map<>::iterator
, mais en utilisantmap<>::iterator
Partout signifie explicitement que vous allez perdre votre temps précieux sur une ondulation de réparation de code mécanique, sauf si un stagiaire passe à côté et que vous pouvez arrêter le travail ennuyeux qui s’y déroule).auto
qu'aucune conversion implicite n'est garantie, cela garantit de meilleures performances par défaut. Si au lieu de cela vous dites le type et qu'il nécessite une conversion, vous obtiendrez souvent une conversion en silence, que vous l'ayez prévu ou non.auto
est votre seule bonne option pour les types difficiles à épeler et indicibles, tels que les lambdas et les aides de modèles, à moins de recourir à desdecltype
expressions répétitives ou à des indirections moins efficacesstd::function
.auto
c’est moins taper. Je mentionne ce dernier point pour compléter, car c’est une raison commune de l’aimer, mais ce n’est pas la plus grande des raisons de l’utiliser.Par conséquent: préférez dire
auto
par défaut. Il offre tellement de simplicité, de performances et de clarté que vous ne vous blessez (et bloquez que les futurs responsables de la maintenance de votre code) ne le fassent pas. Ne commettez sur un type explicite que lorsque vous le pensez vraiment, ce qui signifie presque toujours que vous souhaitez une conversion explicite.Oui, il y a (maintenant) un GotW à ce sujet.
la source
auto x = static_cast<X>(y)
. Lastatic_cast
montre clairement que la conversion est sur le but et il évite les avertissements du compilateur de la conversion. Éviter normalement les avertissements du compilateur n'est pas si bon, mais je suis d'accord avec le fait de ne pas recevoir d'avertissement concernant une conversion que j'ai examinée attentivement lorsque j'ai écritstatic_cast
. Bien que je ne le fasse pas s'il n'y a pas d'avertissement maintenant, mais je veux en avoir à l'avenir si les types changent de manière potentiellement dangereuse.auto
est que nous devrions nous efforcer de programmer contre les interfaces (pas au sens de la POO), pas contre les implémentations spécifiques. C'est la même chose avec les modèles, vraiment. Vous plaignez-vous de "code difficile à lire" parce que vous avez un paramètre de type de modèleT
utilisé partout? Non je ne pense pas. Dans les modèles également, nous codons contre une interface, ce que beaucoup de gens appellent la saisie de données en temps réel.auto
.auto
variable, mais la plupart le font correctement avec une spécification de type explicite. Personne n'utilise IDE? Tout le monde utilise uniquement les variables int / float / bool? Tout le monde préfère une documentation externe pour les bibliothèques plutôt que des en-têtes auto-documentés?=
RHS n’ont pas beaucoup de sens si vous donnez une autre interprétation (barre d’initialisation entre accolades , mais vous devez savoir ce que vous initialisez, ce qui est un oxymoronauto
). Celui qui est surprenant enauto i{1}
déduit égalementinitializer_list
, même si cela implique de ne pas prendre cette liste d'initialisation mais plutôt de prendre cette expression et d'utiliser son type ... mais nousinitializer_list
y arrivons aussi. Heureusement, C ++ 17 résout tout cela bien.C'est une situation au cas par cas.
Cela rend parfois le code plus difficile à comprendre, parfois pas. Prenez, par exemple:
est certainement facile à comprendre et certainement plus facile à écrire que la déclaration de l’itérateur.
J'utilise le C ++ depuis un moment maintenant, mais je peux vous garantir que je commettrais une erreur de compilation dès le premier essai parce que j'oublierais le
const_iterator
et je commencerais par leiterator
... :)Je l'emploierais pour des cas comme celui-ci, mais pas là où il brouille réellement le type (comme dans votre cas), mais c'est purement subjectif.
la source
std::map<int, std::string>::const_iterator
, de sorte que ce n'est pas comme si le nom en disait beaucoup sur le type de toute façon.int
et la valeur eststd::string
. :)it->second
car c'est un itérateur constant. Toutes les informations qui sont une répétition de ce qui est dans la ligne précédente,const std::map<int, std::string>& x
. Dire des choses plusieurs fois informe de temps en temps mieux, mais ce n'est pas une règle générale :-)for (anX : x)
que ce soit encore plus évident: nous ne faisons que répéterx
. Le cas normal où vous avez besoin d' un iterator est quand vous modifiez le conteneur, maisx
estconst&
Regarde ça d'une autre manière. Écris-tu:
ou:
Parfois, il n'est pas utile d'épeler le type explicitement.
Déterminer si vous devez mentionner le type n'est pas la même chose que décider si vous voulez diviser le code en plusieurs instructions en définissant des variables intermédiaires. En C ++ 03, les deux étaient liés, vous pouvez penser
auto
à un moyen de les séparer.Parfois, expliciter les types peut être utile:
contre.
Dans les cas où vous déclarez une variable, utilisez-le pour que
auto
le type reste non prononcé comme dans de nombreuses expressions. Vous devriez probablement essayer de décider vous-même quand cela aide à la lisibilité et quand cela gêne.Vous pouvez affirmer que le mélange de types signés et non signés est une erreur de départ (en fait, certains affirment également qu'il ne faut pas utiliser de types non signés). La raison pour laquelle c'est sans doute une erreur est que cela rend les types d'opérandes d'une importance vitale en raison du comportement différent. Si connaître les types de vos valeurs est une mauvaise chose, ce n’est probablement pas également une mauvaise chose de ne pas avoir besoin de les connaître. Donc, à condition que le code ne prête pas à confusion pour d'autres raisons, ça
auto
va, n'est-ce pas? ;-)En particulier lors de l'écriture de code générique, il existe des cas où le type réel d'une variable ne devrait pas être important, mais ce qui compte, c'est qu'elle réponde à l'interface requise. Donc
auto
fournit un niveau d'abstraction où vous ignorez le type (mais bien sûr, le compilateur ne le sait pas, il le sait). Travailler à un niveau d'abstraction approprié peut aider beaucoup à la lisibilité, travailler au "mauvais" niveau rend la lecture du code un travail fastidieux.la source
auto
vous permet de créer des variables nommées avec des types innommables ou sans intérêt. Des noms significatifs peuvent être utiles.sizeof
non signé sur vous.OMI, vous regardez à peu près à l'inverse.
Il ne s'agit pas de
auto
conduire à un code illisible ou encore moins lisible. C'est une question de (espérer que) avoir un type explicite pour la valeur de retour compensera le fait que (apparemment) ce n'est pas le type qui serait retourné par une fonction particulière.Au moins à mon avis, si vous avez une fonction dont le type de retour n'est pas immédiatement évident, c'est là votre problème. Ce que fait la fonction doit être évident à partir de son nom et le type de la valeur de retour doit être évident à partir de ce qu'il fait. Sinon, c'est la vraie source du problème.
S'il y a un problème ici, ce n'est pas avec
auto
. C'est avec le reste du code, et il y a de bonnes chances que le type explicite soit juste un pansement pour vous empêcher de voir et / ou de résoudre le problème principal. Une fois que vous avez résolu ce problème, la lisibilité du codeauto
sera généralement satisfaisante.Je suppose qu'en toute justice, je devrais ajouter: j'ai déjà traité quelques cas dans lesquels de telles choses n'étaient pas aussi évidentes que vous le souhaiteriez, et la résolution du problème était également assez intenable. Pour ne citer qu'un exemple, j'ai effectué quelques consultations pour une entreprise qui avait fusionné avec une autre entreprise il y a quelques années. Ils se sont retrouvés avec une base de code qui était plus "combinée" que réellement fusionnée. Les programmes constitutifs avaient commencé par utiliser des bibliothèques différentes (mais assez similaires) à des fins similaires, et même s’ils travaillaient à une fusion plus nette, ils l’avaient toujours fait. Dans bon nombre de cas, le seul moyen de deviner quel type serait renvoyé par une fonction donnée était de savoir d'où venait cette fonction.
Même dans un tel cas, vous pouvez contribuer à clarifier un certain nombre de choses. Dans ce cas, tout le code a commencé dans l'espace de noms global. En déplaçant simplement une quantité importante dans certains espaces de noms, vous avez également éliminé les conflits de noms et simplifié également beaucoup le suivi des types.
la source
Il y a plusieurs raisons pour lesquelles je n'aime pas l'auto à usage général:
Mais attendez, est-ce vraiment une bonne idée? Et si le type importait dans une demi-douzaine de ces cas d'utilisation, et que ce code se comporte réellement différemment? Cela peut aussi implicitement rompre l’encapsulation, en modifiant non seulement les valeurs d’entrée, mais également le comportement de l’implémentation privée d’autres classes qui appellent la fonction.
1a. Je crois au concept de "code auto-documenté". La raison derrière le code auto-documenté est que les commentaires ont tendance à devenir obsolètes, ne reflétant plus ce que fait le code, alors que le code lui-même - s'il est écrit de manière explicite - est explicite, reste toujours à jour sur son intention, et ne vous laissera pas confus avec des commentaires obsolètes. Si les types peuvent être modifiés sans avoir à modifier le code lui-même, le code / les variables elles-mêmes peuvent devenir obsolètes. Par exemple:
auto bThreadOK = CheckThreadHealth ();
Sauf que le problème est que CheckThreadHealth () a été refactored à un moment donné pour renvoyer une valeur enum indiquant le statut d'erreur, le cas échéant, au lieu d'une valeur booléenne. Mais la personne qui a effectué cette modification a manqué d'inspecter cette ligne de code et le compilateur n'a pas aidé, car elle a été compilée sans avertissements ni erreurs.
Cela fonctionne même, probablement. Je dis un peu ça marche, parce que même si vous faites une copie d'une structure de 500 octets pour chaque itération de boucle, de sorte que vous puissiez inspecter une seule valeur dessus, le code est toujours complètement fonctionnel. Ainsi, même vos tests unitaires ne vous aident pas à vous rendre compte que le mauvais code se cache derrière cette auto simple et innocente. La plupart des autres personnes parcourant le fichier ne le remarqueront pas non plus à première vue.
Cela peut également être aggravé si vous ne connaissez pas le type, mais que vous choisissez un nom de variable qui fait fausse hypothèse sur son contenu, obtenant en fait le même résultat que dans 1a, mais dès le début plutôt que post-refactor.
Il me semble évident que auto a été introduit principalement comme solution de contournement pour la syntaxe terrible avec les types de gabarit de bibliothèque standard. Plutôt que d'essayer de corriger la syntaxe de modèle que les gens connaissent déjà - ce qui peut aussi être presque impossible à faire à cause de tout le code existant qu'il pourrait endommager - ajoutez un mot-clé qui cache le problème. Essentiellement ce que vous pourriez appeler un "piratage".
En fait, je n’ai aucun désaccord avec l’utilisation de auto avec les conteneurs de bibliothèque standard. Il est évident que le mot clé a été créé et que les fonctions de la bibliothèque standard ne risquent pas de changer fondamentalement d’objet (ni de type, par conséquent), ce qui rend l’utilisation de l’auto relativement sûre. Mais je serais très prudent de l’utiliser avec votre propre code et des interfaces qui pourraient être beaucoup plus volatiles et potentiellement sujettes à des changements plus fondamentaux.
Une autre application utile de auto qui améliore les capacités du langage est la création de temporaires dans des macros indépendantes du type. C'est quelque chose que vous ne pouviez pas vraiment faire auparavant, mais vous pouvez le faire maintenant.
la source
auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);
. :-)Oui, il est plus facile de connaître le type de votre variable si vous ne l'utilisez pas
auto
. La question est: avez-vous besoin de connaître le type de votre variable pour lire le code? Parfois, la réponse sera oui, parfois non. Par exemple, lorsque vous obtenez un itérateur d'unstd::vector<int>
, avez-vous besoin de savoir que c'est unstd::vector<int>::iterator
ouauto iterator = ...;
suffirait? Tout ce que tout le monde voudrait faire avec un itérateur est donné par le fait que c'est un itérateur - peu importe le type utilisé.Utilisez-le
auto
dans les situations où cela ne rend pas votre code plus difficile à lire.la source
Personnellement, je n'utilise
auto
que lorsqu'il est absolument évident pour le programmeur de quoi il s'agit.Exemple 1
Exemple 2
la source
auto record = myObj.FindRecord(something)
il serait clair que le type de variable était record. Ou le nommer ou un nomit
similaire indiquerait clairement qu'il renvoie un itérateur. Notez que, même si vous ne l'utilisiez pasauto
, nommer correctement la variable signifierait qu'il n'est pas nécessaire de revenir à la déclaration pour consulter le type à partir de n'importe où dans la fonction . J'ai retiré mon vote négatif parce que l'exemple n'est pas complet maintenant, mais je n'achète toujours pas l'argument ici.MyClass::RecordTy record = myObj.FindRecord (something)
Cette question sollicite l'opinion, qui variera d'un programmeur à l'autre, mais je dirais non. En fait, dans de nombreux cas, le contraire
auto
peut aider à rendre le code plus facile à comprendre en permettant au programmeur de se concentrer sur la logique plutôt que sur la minutie.Cela est particulièrement vrai dans le cas de types de modèles complexes. Voici un exemple simplifié et artificiel. Lequel est plus facile à comprendre?
.. ou...
Certains diraient que le second est plus facile à comprendre, d'autres peuvent dire le premier. D'autres encore pourraient dire qu'une utilisation gratuite de
auto
pourrait contribuer à réduire les coûts des programmeurs qui l'utilisent, mais c'est une autre histoire.la source
std::map
exemples, ainsi que des arguments de modèles complexes.map
s. :)for
par exemple si les itérateurs sont invalidés dans le corps de la boucle et doivent donc être pré-incrémentés ou ne pas être incrémentés du tout.Beaucoup de bonnes réponses à ce jour, mais pour me concentrer sur la question initiale, je pense que Herb va trop loin dans ses conseils pour une utilisation
auto
généreuse. Votre exemple est un cas où l'utilisationauto
nuit évidemment à la lisibilité. Certaines personnes insistent sur le fait que les IDE modernes ne posent pas problème, car on peut survoler une variable et voir le type, mais je ne suis pas d'accord: même les personnes qui utilisent toujours un IDE ont parfois besoin de regarder des extraits de code de manière isolée (pensez aux critiques de code). , par exemple) et un IDE ne va pas aider.En bout de ligne: à utiliser
auto
quand cela aide: les itérateurs dans les boucles for. Ne l'utilisez pas lorsque le lecteur a du mal à trouver le type.la source
Je suis assez surpris que personne n'ait encore signalé que auto aide s'il n'y a pas de texte clair. Dans ce cas, vous pouvez contourner ce problème en utilisant un #define ou un typedef dans un modèle pour trouver le type utilisable (et ce n'est parfois pas trivial), ou vous utilisez simplement auto.
Supposons que vous ayez une fonction qui retourne quelque chose avec un type spécifique à la plate-forme:
Utilisation de la sorcière préférez-vous?
ou tout simplement
Bien sûr, vous pouvez écrire
aussi bien quelque part, mais
en dire plus sur le type de x? Il indique que c'est ce qui est renvoyé à partir de là, mais c'est exactement ce que l'auto est. Ceci est simplement redondant - le mot "substance" est écrit 3 fois ici - ce qui, à mon avis, le rend moins lisible que la version "auto".
la source
typedef
utiliser.La lisibilité est subjective. vous devrez examiner la situation et décider ce qui convient le mieux.
Comme vous l'avez fait remarquer, sans auto, de longues déclarations peuvent générer beaucoup de fouillis. Mais comme vous l'avez également souligné, de courtes déclarations peuvent supprimer des informations de type qui peuvent être utiles.
En plus de cela, je voudrais aussi ajouter ceci: assurez-vous que vous regardez la lisibilité et non l'écriture. Un code facile à écrire n'est généralement pas facile à lire et vice versa. Par exemple, si j'écrivais, je préférerais auto. Si je lisais, peut-être les déclarations les plus longues.
Ensuite, il y a la cohérence; à quel point est-ce important pour vous? Voulez-vous l'auto dans certaines parties et les déclarations explicites dans d'autres, ou une méthode cohérente dans l'ensemble?
la source
Je prendrai comme avantage un code moins lisible et encouragerai le programmeur à l’utiliser de plus en plus. Pourquoi? Clairement, si le code utilisant auto est difficile à lire, il sera également difficile à écrire. Le programmeur est obligé d'utiliser le nom de variable explicite pour améliorer son travail.
Peut-être qu'au début, le programmeur pourrait ne pas écrire les noms de variables significatifs. Mais finalement, lors de la correction des bugs, ou lors de la révision du code, quand il / elle doit expliquer le code à d’autres, ou dans un avenir pas si proche, il / elle explique le code aux personnes chargées de la maintenance, le programmeur réalisera l’erreur et utilisera le nom de la variable significative à l'avenir.
la source
myComplexDerivedType
pour compenser le type manquant, ce qui encombre le code par la répétition de types (partout où la variable est utilisée) et qui incite les gens à omettre le but de la variable dans son nom. . Mon expérience est qu'il n'y a rien d'aussi improductif que de mettre activement des obstacles dans le code.J'ai deux lignes directrices:
Si le type de variable est évident, fastidieux à écrire ou difficile à déterminer, utilisez auto.
Si vous avez besoin d’une conversion spécifique ou si le type de résultat n’est pas évident et pourrait prêter à confusion.
la source
Oui. Cela diminue la verbosité mais le malentendu courant est que la verbosité diminue la lisibilité. Cela n'est vrai que si vous considérez la lisibilité comme étant esthétique plutôt que votre capacité réelle à interpréter du code - qui n'est pas augmentée par l'utilisation de auto. Dans l'exemple le plus souvent cité, les itérateurs de vecteurs, il peut apparaître à la surface que l'utilisation de l'option auto augmente la lisibilité de votre code. D'autre part, vous ne savez pas toujours ce que le mot-clé auto vous donnera. Vous devez suivre le même chemin logique que le compilateur pour effectuer cette reconstruction interne, et la plupart du temps, en particulier avec les itérateurs, vous ferez de fausses hypothèses.
En fin de compte, "auto" sacrifie la lisibilité du code et la clarté, pour la "propreté" syntaxique et esthétique (nécessaire uniquement parce que les itérateurs ont une syntaxe inutilement compliquée) et la possibilité de taper 10 caractères de moins sur une ligne donnée. Cela ne vaut pas le risque, ni l'effort impliqué à long terme.
la source