Est-ce que l'auto rend le code C ++ plus difficile à comprendre?

122

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 varutilisation, 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 autocode C ++ plus difficile à comprendre.

Mircea Ispas
la source
29
Avez-vous vraiment besoin de vérifier le type de retour à chaque fois ? Pourquoi le type n'est-il pas clair dans le code? autopeut 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.
R. Martinho Fernandes
25
"L'art" d'utiliser autoest un peu comme déterminer quand utiliser typedef. C'est à vous de déterminer quand cela vous gêne et quand cela vous aidera.
ahenderson
18
Je pensais avoir le même problème, mais j'ai alors réalisé que je pouvais comprendre le code sans connaître les types. par exemple: "auto idx = get_index ();" alors idx est quelque chose qui tient un index. Le type exact n'est pas pertinent dans la plupart des cas.
PlasmaHH
31
Alors n'écrivez pas auto x = GetX();, choisissez un meilleur nom que xcelui qui vous dit ce qu'il fait dans ce contexte spécifique ... c'est souvent plus utile que son type de toute façon.
Jonathan Wakely
11
Si l'utilisation d'un plus grand nombre d'inférences rend difficile la lecture du code par un programmeur, le code ou le programmeur doit être sérieusement amélioré.
CA McCann

Réponses:

100

Réponse courte: Plus complètement, mon opinion actuelle autoest que vous devriez utiliser autopar 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:

  • (Commun) La initializer_listsurprise qui en auto x = { 1 };découle initializer_list. Si vous ne voulez pas initializer_list, dites le type - c.-à-d., Demandez explicitement une conversion.
  • (Rare) Le cas des modèles d'expression, tel que celui qui 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 autopar défaut sinon, car cela autoé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":

  • Correctness: En utilisant des autogaranties, 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'écrire const_iterator(avez-vous?), Alors que autotout se passe bien.
  • Facilité de maintenance et robustesse: l' utilisation autorend votre code plus robuste face aux changements car, lorsque le type de l'expression change, autole 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 un mapen un unordered_map, ce qui est toujours correct si vous ne vous fiez pas à l'ordre, en utilisant autopour vos itérateurs vous passerez de manière transparente map<>::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).
  • Performance: Parce autoqu'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.
  • Convivialité: l’ utilisation autoest votre seule bonne option pour les types difficiles à épeler et indicibles, tels que les lambdas et les aides de modèles, à moins de recourir à des decltypeexpressions répétitives ou à des indirections moins efficaces std::function.
  • Commodité: Et oui, autoc’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 autopar 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.

Herb Sutter
la source
14
Je trouve auto utile même quand je veux une conversion. Il me permet de demander explicitement une conversion sans répéter le type: auto x = static_cast<X>(y). La static_castmontre 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 écrit static_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.
Bjarke Hammersholt Roune Le
6
Une chose que je trouve avec autoest 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èle Tutilisé 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.
Xeo
6
"Utiliser Auto garantit que vous aurez le bon type." Pas vrai du tout. Cela garantit uniquement que vous obtiendrez le type prescrit par une autre partie de votre code. Que ce soit vrai ou non est totalement incertain quand vous le cachez derrière auto.
Courses de légèreté en orbite
Je suis vraiment surpris que personne ne se soucie des IDE ... Même les IDE modernes ne supportent pas correctement le passage à la définition de classe / struct en cas de autovariable, 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?
Avtomaton
GotW: herbsutter.com/2013/08/12/… Je ne vois pas en quoi cette "surprise initializer_list" est une surprise; les accolades autour de =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 oxymoron auto). Celui qui est surprenant en auto i{1}déduit également initializer_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 nous initializer_listy arrivons aussi. Heureusement, C ++ 17 résout tout cela bien.
underscore_d
112

C'est une situation au cas par cas.

Cela rend parfois le code plus difficile à comprendre, parfois pas. Prenez, par exemple:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

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_iteratoret je commencerais par le iterator... :)

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.

Luchian Grigore
la source
45
Exactement. Qui diable se soucie du type. C'est un itérateur. Peu m'importe le type, tout ce que j'ai besoin de savoir, c'est que je peux l'utiliser pour itérer.
R. Martinho Fernandes
5
+1 Même si vous nommiez le type, vous le nommeriez comme 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.
Steve Jessop
4
@SteveJessop: Cela me dit au moins deux choses: la clé est intet la valeur est std::string. :)
Nawaz
16
@Nawaz: et que vous ne pouvez pas assigner it->secondcar 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 :-)
Steve Jessop
11
TBH Je préférerais for (anX : x)que ce soit encore plus évident: nous ne faisons que répéter x. Le cas normal où vous avez besoin d' un iterator est quand vous modifiez le conteneur, mais xestconst&
MSalters
94

Regarde ça d'une autre manière. Écris-tu:

std::cout << (foo() + bar()) << "\n";

ou:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

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:

// seems legit    
if (foo() < bar()) { ... }

contre.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

Dans les cas où vous déclarez une variable, utilisez-le pour que autole 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 autova, 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 autofournit 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.

Steve Jessop
la source
21
+1 autovous permet de créer des variables nommées avec des types innommables ou sans intérêt. Des noms significatifs peuvent être utiles.
Mankarse
Mélange signé et non signé si vous utilisez non signé pour un usage correct: arithmétique modulaire. Ce n'est pas si vous utilisez mal non signé pour un entier positif. Presque aucun programme n’a d’utilisation pour non signé, mais le langage de base impose sa définition inepte de sizeofnon signé sur vous.
Curiousguy
27

OMI, vous regardez à peu près à l'inverse.

Il ne s'agit pas de autoconduire à 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 code autosera 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.

Jerry Coffin
la source
17

Il y a plusieurs raisons pour lesquelles je n'aime pas l'auto à usage général:

  1. Vous pouvez refactoriser le code sans le modifier. Oui, c’est l’un des avantages souvent mentionnés de l’utilisation de l’auto. Il suffit de changer le type de retour d'une fonction et si tout le code qui l'appelle utilise auto, aucun effort supplémentaire n'est requis! Vous appuyez sur Compile, cela crée - 0 avertissements, 0 erreurs - et vous ne faites que vérifier votre code sans avoir à gérer le fouillis lié à la recherche et à la modification éventuelle des 80 emplacements utilisés par la fonction.

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.

  1. Vous pouvez ne jamais savoir quels sont les types réels. Ceci est également souvent mentionné comme un "avantage" principal de auto. Pourquoi apprendre ce qu'une fonction vous donne, quand vous pouvez simplement dire: "Qui est-ce qui compte? Ça compile!"

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.

  1. Taper le code lors de la première écriture n’est pas la partie la plus longue de la programmation. Oui, auto rend l'écriture de code plus rapide au départ. En termes de non-responsabilité, je tape> 100 WPM, alors peut-être que ça ne me dérange pas autant que les autres. Mais si tout ce que j'avais à faire était d'écrire du nouveau code toute la journée, je serais un campeur heureux. La partie la plus fastidieuse de la programmation est le diagnostic de bogues difficiles à reproduire dans le code, souvent dus à des problèmes subtils et non évidents - tels que le type de surutilisation de auto est susceptible d’introduire (référence signé vs non signé, float vs int, bool vs pointeur, etc.).

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.

Tim W
la source
4
Vous avez réussi. J'aimerais pouvoir donner à cela un +2.
cmaster
Une bonne réponse "sois prudente". @ cmaster: ça y est.
Déduplicateur
Je l' ai trouvé un cas plus utile: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist
14

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'un std::vector<int>, avez-vous besoin de savoir que c'est un std::vector<int>::iteratorou auto 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 autodans les situations où cela ne rend pas votre code plus difficile à lire.

Joseph Mansfield
la source
12

Personnellement, je n'utilise autoque lorsqu'il est absolument évident pour le programmeur de quoi il s'agit.

Exemple 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Exemple 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

la source
5
Ceci est un exemple clair de mauvaise désignation affectant la lisibilité, pas vraiment automatique. Personne n'a la moindre idée de ce que fait "DoSomethingWeird", donc utiliser auto ou non ne le rendra pas plus lisible. De toute façon, vous devrez vérifier la documentation.
R. Martinho Fernandes
4
Ok, c'est un peu mieux maintenant. Je trouve toujours que la variable est mal nommée, ce qui fait encore mal. Si vous deviez écrire, auto record = myObj.FindRecord(something)il serait clair que le type de variable était record. Ou le nommer ou un nom itsimilaire indiquerait clairement qu'il renvoie un itérateur. Notez que, même si vous ne l'utilisiez pas auto, 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.
R. Martinho Fernandes
2
Pour ajouter à @ R.MartinhoFernandes: la question est: est-ce vraiment important maintenant QU'EST-CE QU'UN "ENREGISTREMENT"? Je pense qu'il est plus important que ce soit un enregistrement, le type primitif sous-jacent actuel est une autre couche d'abstraction. Donc, on aurait probablement sans auto:MyClass::RecordTy record = myObj.FindRecord (something)
paul23
2
@ paul23: Qu'est-ce que l'utilisation de auto par rapport au type vous apporte, alors, si votre seule objection est "je ne sais pas comment utiliser cela". De toute façon, vous le regardez quand même.
GManNickG
3
@GManNickG il me dit le type exact de manque d'importance.
paul23
10

Cette question sollicite l'opinion, qui variera d'un programmeur à l'autre, mais je dirais non. En fait, dans de nombreux cas, le contraire autopeut 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?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. ou...

for( auto it = things_.begin(); it != things_.end(); ++it )

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 autopourrait contribuer à réduire les coûts des programmeurs qui l'utilisent, mais c'est une autre histoire.

John Dibling
la source
4
+1 Haha, tout le monde présente des std::mapexemples, ainsi que des arguments de modèles complexes.
Nawaz
1
@Nawaz: Il est facile de trouver des noms de modèles très longs en utilisant maps. :)
John Dibling
@Nawaz: mais je me demande pourquoi personne ne vient avec la gamme basée sur les boucles, une alternative plus lisible et plus lisible ...
PlasmaHH
1
@PlasmaHH, toutes les boucles avec itérateurs ne peuvent pas être remplacées par des plages, forpar 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.
Jonathan Wakely
@PlasmaHH: Dans mon cas, le MSVC10 ne fait pas de boucles for basées sur la distance. Etant donné que MSVC10 est mon banc d'essai C ++ 11, je n'ai pas beaucoup d'expérience avec eux.
John Dibling
8

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 autogénéreuse. Votre exemple est un cas où l'utilisation autonuit é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 autoquand cela aide: les itérateurs dans les boucles for. Ne l'utilisez pas lorsque le lecteur a du mal à trouver le type.

Nemanja Trifunovic
la source
6

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:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Utilisation de la sorcière préférez-vous?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

ou tout simplement

auto stuff = getStuff();

Bien sûr, vous pouvez écrire

#define StuffType (...)

aussi bien quelque part, mais

StuffType stuff = getStuff();

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".

Lrdx
la source
5
La bonne façon de gérer les types spécifiques à une plate-forme est de les typedefutiliser.
cmaster
3

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
2

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.

Manoj R
la source
2
Au mieux, vous feriez en sorte que les gens écrivent des noms de variables comme myComplexDerivedTypepour 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.
cmaster
2

J'ai deux lignes directrices:

  • Si le type de variable est évident, fastidieux à écrire ou difficile à déterminer, utilisez auto.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
    
  • 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.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious
    
Rouge XIII
la source
0

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.

métamorphose
la source