Combien coûte trop avec le mot-clé automatique C ++ 11?

J’ai utilisé le nouveau mot auto clé auto disponible dans la norme C ++ 11 pour les types compliqués de modèles, et c’est ce pour quoi je pense qu’il a été conçu. Mais je l’utilise aussi pour des choses comme:

 auto foo = std::make_shared(); 

Et plus sceptiquement pour:

 auto foo = bla(); // where bla() return a shared_ptr 

Je n’ai pas vu beaucoup de discussion sur ce sujet. Il semble que l’ auto puisse être surutilisée, car un type est souvent une forme de documentation et de vérification de la santé mentale. Où tracez-vous la ligne en utilisant auto et quels sont les cas d’utilisation recommandés pour cette nouvelle fonctionnalité?

Pour clarifier: je ne demande pas un avis philosophique; Je demande l’utilisation prévue de ce mot-clé par le comité standard, éventuellement avec des commentaires sur la manière dont cet usage prévu est réalisé dans la pratique.

Note latérale: Cette question a été déplacée vers SE.Programmers, puis de nouveau vers Stack Overflow. La discussion à ce sujet peut être trouvée dans cette méta-question .

Je pense que l’on devrait utiliser le mot auto clé auto chaque fois qu’il est difficile de dire comment écrire le type à première vue, mais le type du côté droit d’une expression est évident. Par exemple, en utilisant:

 my_multi_type::nth_index<2>::type::key_type::composite_key_type::\ key_extractor_tuple::tail_type::head_type::result_type 

pour obtenir le type de clé composite dans boost::multi_index , même si vous savez qu’il est int . Vous ne pouvez pas simplement écrire int car il pourrait être modifié dans le futur. Je voudrais écrire auto dans ce cas.

Donc, si le mot auto clé auto améliore la lisibilité dans un cas particulier, utilisez-le. Vous pouvez écrire auto quand il est évident pour le lecteur quel type auto représente.

Voici quelques exemples:

 auto foo = std::make_shared(); // obvious auto foo = bla(); // unclear. don't know which type `foo` has const size_t max_size = 100; for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors // since max_size is unsigned std::vector v; for ( auto it = v.begin(); it != v.end(); ++it ) // ok, since I know // that `it` has an iterator type (don't really care which one in this context) 

Utilisez l’ auto partout où vous le pouvez, en particulier l’ const auto pour que les effets secondaires soient moins préoccupants. Vous n’aurez pas à vous soucier des types, sauf dans les cas évidents, mais ils seront toujours vérifiés statiquement pour vous et vous éviterez certaines répétitions. Lorsque auto n’est pas réalisable, vous pouvez utiliser decltype pour exprimer les types de manière sémantique en tant que contrats basés sur des expressions. Votre code sera différent, mais ce sera un changement positif.

Facile. Utilisez-le lorsque vous ne vous souciez pas du type. Par exemple

 for (const auto & i : some_container) { ... 

Tout ce qui m’intéresse, c’est que c’est ce que i trouve dans le conteneur.

C’est un peu comme les typedefs.

 typedef float Height; typedef double Weight; //.... Height h; Weight w; 

Ici, peu m’importe si h et w sont des flotteurs ou des doubles, mais seulement qu’ils sont de type quelconque pour exprimer des hauteurs et des poids .

Ou considérez

 for (auto i = some_container .begin (); ... 

Ici, tout ce qui m’importe, c’est que c’est un iterator approprié, supportant operator++() , c’est un peu comme le type de canard à cet égard.

De plus, le type de lambda ne peut pas être épelé, donc auto f = []... est un bon style. L’alternative consiste à std::function mais cela vient avec la surcharge.

Je ne peux pas vraiment concevoir un “abus” de l’ auto . Le plus proche que je puisse imaginer est de vous priver d’une conversion explicite à un type significatif – mais vous n’utiliseriez pas auto pour cela, vous construiriez un object du type souhaité.

Si vous pouvez supprimer une certaine redondance dans votre code sans introduire d’effets secondaires, vous devez le faire.

Contre-exemples (empruntés aux réponses de quelqu’un d’autre):

 auto i = SomeClass(); for (auto x = make_unsigned (y); ...) 

Ici, on se soucie du type, donc on doit écrire Someclass i; et for(unsigned x = y;...

Fonce. Utilisez auto n’importe où pour faciliter l’écriture du code.

Chaque nouvelle fonctionnalité dans n’importe quelle langue va être surutilisée par au moins certains types de programmeurs. Ce n’est que par une utilisation modérée par certains programmeurs expérimentés (et non par des programmeurs) que le rest des programmeurs expérimentés apprend les limites de l’utilisation correcte. La surutilisation extrême est généralement mauvaise, mais pourrait être une bonne chose car une telle surutilisation peut conduire à des améliorations de la fonctionnalité ou à une meilleure fonctionnalité pour la remplacer.

Mais si je travaillais sur du code avec plus de quelques lignes comme

 auto foo = bla(); 

Si le type est indiqué zéro fois, je souhaiterais peut-être modifier ces lignes pour inclure des types. Le premier exemple est excellent car le type est indiqué une fois et nous évite de devoir écrire deux types de modèles en désordre. Hourra pour C ++++. Mais montrer explicitement le type fois fois, si ce n’est pas facilement visible dans une ligne proche, me rend nerveux, au moins en C ++ et ses successeurs immédiats. Pour les autres langages conçus pour travailler à un niveau supérieur avec plus d’abstraction, de polymorphism et de généricité, c’est bien.

Oui, on peut en abuser au désortingment de la lisibilité. Je suggère de l’utiliser dans des contextes où les types exacts sont longs ou inexprimables, ou peu importants pour la lisibilité, et les variables sont de courte durée. Par exemple, le type d’iterator est généralement long et n’est pas important, donc auto fonctionnerait:

  for(auto i = container.begin(); i != container.end(); ++i); 

auto ici ne nuit pas à la lisibilité.

Un autre exemple est le type de règle d’parsing, qui peut être long et compliqué. Comparer:

  auto spaces = space & space & space; 

avec

 r_and_t&, r_char_t&>, r_char_t&> spaces = space & space & space; 

D’un autre côté, quand le type est connu et simple, c’est bien mieux s’il est dit explicitement:

 int i = foo(); 

plutôt que

 auto i = foo(); 

Au C ++ et au-delà de 2012 dans le panel Ask Us Anything , il y a eu un échange fantastique entre Andrei Alexandrescu, Scott Meyers et Herb Sutter qui a parlé de l’utilisation et non de l’ auto . Passez à minute 25:03 pour une discussion de 4 minutes. Les trois haut-parleurs donnent d’excellents points à retenir pour ne pas utiliser l’ auto .

J’encourage fortement les gens à tirer leurs propres conclusions, mais mon objective était d’ utiliser l’ auto partout sauf si :

  1. Ça fait mal la lisibilité
  2. On s’inquiète de la conversion de type automatique (par exemple, des constructeurs, des affectations, des types intermédiaires de modèles, des conversions implicites entre des largeurs entières)

L’utilisation par les libéraux des services explicit consortingbue à réduire les inquiétudes pour ces derniers, ce qui consortingbue à réduire au minimum la durée de la première.

En reformulant ce que Herb a dit, “si vous ne faites pas X, Y et Z, utilisez auto . Apprenez ce que X, Y et Z sont et allez de l’avant et utilisez auto partout ailleurs.”

auto peut être très dangereux en combinaison avec des modèles d’expression très utilisés par les bibliothèques d’algèbre linéaire telles qu’Eigen ou OpenCV.

 auto A = Masortingx(...); auto B = Masortingx(...); auto C = A * B; // C is not a masortingx. It is a masortingx EXPRESSION. cout << C; // The expression is evaluated and gives the expected result. ... //  cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result. 

Les bugs causés par ce type d'erreurs sont une douleur majeure pour le débogage. Une solution possible consiste à convertir explicitement le résultat au type attendu si vous ne voulez pas utiliser auto pour le style de déclaration de gauche à droite.

 auto C = Masortingx(A * B); // The expression is now evaluated immediately. 

J’utilise l’ auto sans ressortingction et je n’ai rencontré aucun problème. Je finis même parfois par l’utiliser pour des types simples comme int . Cela rend c ++ un langage de niveau supérieur pour moi, et permet de déclarer une variable en c ++ comme en python. Après avoir écrit du code python, j’écris même parfois par ex.

 auto i = MyClass(); 

au lieu de

 MyClass i; 

C’est un cas où je dirais que c’est un abus du mot auto clé auto .

Souvent, je ne me soucie pas du type exact de l’object, je suis plus intéressé par sa fonctionnalité, et comme les noms de fonctions disent généralement quelque chose sur les objects qu’ils renvoient, auto ne fait pas de mal: par exemple auto s = mycollection.size() , Je peux deviner que s sera une sorte d’entier, et dans les rares cas où je me soucie du type exact, vérifions alors le prototype de fonction (je veux dire, je préfère vérifier quand j’ai besoin de l’info, plutôt qu’à priori quand le code est écrit, juste au cas où il serait utile un jour, comme dans int_type s = mycollection.size() ).

Concernant cet exemple de la réponse acceptée:

 for ( auto x = max_size; x > 0; --x ) 

Dans mon code, j’utilise toujours auto dans ce cas, et si je veux que x soit non signé, j’utilise une fonction utilitaire, nommée make_unsigned , qui exprime clairement mes préoccupations:

 for ( auto x = make_unsigned(max_size); x > 0; --x ) 

disclaimer: Je viens de décrire mon utilisation, je ne suis pas compétent pour donner des conseils!

Un danger que j’ai noté est en termes de références. par exemple

 MyBigObject& ref_to_big_object= big_object; auto another_ref = ref_to_big_object; // ? 

Le problème est que another_ref n’est pas une référence dans ce cas, il s’agit de MyBigObject au lieu de MyBigObject &. Vous finissez par copier un gros object sans vous en rendre compte.

Si vous obtenez une référence directement à partir d’une méthode, vous pourriez ne pas penser à ce que c’est réellement.

 auto another_ref = function_returning_ref_to_big_object(); 

vous auriez besoin de “auto &” ou “const auto &”

 MyBigObject& ref_to_big_object= big_object; auto& another_ref = ref_to_big_object; const auto& yet_another_ref = function_returning_ref_to_big_object(); 

L’un des problèmes majeurs du programme C ++ est qu’il vous permet d’utiliser la variable non initialisée . Cela nous amène à un comportement de programme non déterministe désagréable. Il convient de noter que le compilateur moderne lance maintenant des messages d’avertissement appropriés / de message si le programme se fatigue à les utiliser.

Pour illustrer ceci, considérons ci-dessous le programme c ++:

 int main() { int x; int y = 0; y += x; } 

Si je comstack ce programme en utilisant le compilateur moderne (GCC), il donne l’avertissement. Un tel avertissement peut ne pas être très évident si nous travaillons avec le vrai code de production complexe.

main.cpp: Dans la fonction ‘int main ()’:

main.cpp: 4: 8: avertissement : ‘x’ est utilisé non initialisé dans cette fonction [-Wuninitialized]

y + = x;

  ^ 

================================================== =============================== Maintenant, si nous modifions notre programme qui utilise l’ auto , comstackz nous pour obtenir ce qui suit:

 int main() { auto x; auto y = 0; y += x; } 

main.cpp: Dans la fonction ‘int main ()’:

main.cpp: 2: 10: erreur : la déclaration de ‘auto x’ n’a pas d’initialiseur

  auto x; ^ 

Avec auto, il n’est pas possible d’utiliser la variable non initialisée. C’est un avantage majeur que nous pouvons obtenir (gratuitement) si nous commençons à utiliser l’ auto .

Ce concept et d’autres grands concepts modernes de C ++ sont expliqués par Herb Shutter, expert en C ++, dans son exposé sur CppCon14 :

Retour à l’essentiel! Les bases du style C ++ moderne

Utilisez auto où il est judicieux de déduire un type. Si vous avez quelque chose que vous savez être un nombre entier, ou si vous savez que c’est une chaîne, utilisez simplement int / std :: ssortingng, etc. Je ne m’inquiéterais pas de la “surutilisation” d’une fonctionnalité de langue si elle n’est pas ridicule. ou obfuscate le code.

C’est mon avis quand même.

auto mot auto clé auto ne peut être utilisé que pour une variable locale, pas pour des arguments ou des membres class / struct. Donc, il est sûr et viable de les utiliser où vous voulez. Je les utilise beaucoup. Le type est déduit au moment de la compilation, le débogueur affiche le type lors du débogage, la sizeof signale correctement, le decltype donnera le type correct – il n’y a pas de problème. Je ne compte pas l’ auto comme surutilisé, jamais!

TL; DR: Voir la règle générale en bas.

La réponse acceptée suggère la règle suivante:

Utilisez auto quand il est difficile de dire comment écrire le type à première vue, mais le type du côté droit d’une expression est évident.

Mais je dirais que c’est trop ressortingctif. Parfois, je ne me soucie pas des types, car la déclaration est suffisamment informative sans que je me donne la peine de prendre le temps de comprendre le type. Qu’est-ce que je veux dire par là? Considérez l’exemple qui a surgi dans certaines des réponses:

 auto x = f(); 

Qu’est-ce qui en fait un exemple de mauvaise utilisation de l’ auto ? Est-ce mon ignorance du type de retour de f() ? Eh bien, cela pourrait en effet aider si je le savais, mais – ce n’est pas ma principale préoccupation. Ce qui est beaucoup plus problématique, c’est que x et f() n’ont pas de sens. Si nous avions:

 auto nugget = mine_gold(); 

au lieu de cela, je ne me soucie généralement pas si le type de retour de la fonction est évident ou non. En lisant la déclaration, je sais ce que je fais et j’en sais assez sur la sémantique de la valeur de retour pour ne pas avoir besoin de connaître son type.

Donc, ma réponse est la suivante: Utilisez auto chaque fois que le compilateur le permet, sauf si:

  • Vous estimez que le nom de la variable ainsi que l’expression d’initialisation / affectation ne fournissent pas suffisamment d’informations sur ce que fait la déclaration.
  • Vous estimez que le nom de la variable, associé à l’expression d’initialisation / affectation, fournit des informations «trompeuses» sur le type – c.-à-d. Si vous deviez deviner ce qui vient au lieu de l’auto, mal, et cette fausse hypothèse a des répercussions plus tard dans le code.
  • Vous voulez forcer un type différent (par exemple une référence).

Et aussi:

  • Préférez donner un nom significatif (qui ne contient pas le nom du type bien sûr) avant de remplacer auto par le type concret.

Une de mes expériences amères avec auto utilise avec les expressions lambda :

 auto i = []() { return 0; }; cout<<"i = "< 

En fait, ici, i résolu à utiliser le pointeur de l' int(*)() . Ceci est juste un simple cout , mais imaginez juste quel genre de mauvaises erreurs de compilation / runtime il peut causer lorsqu'il est utilisé avec template .

Vous devriez éviter l' auto avec de telles expressions et mettre un type de return correct (ou un decltype() contrôlé)

L'utilisation correcte pour l'exemple ci-dessus serait,

 auto i = []() { return 0; }(); // and now i contains the result of calling the lambda