Le nouveau mot-clé “auto”; Quand devrait-il être utilisé pour déclarer un type de variable?

Duplication possible:
Combien coûte trop avec le mot clé automatique C ++ 0x

Avons-nous (en tant que communauté) assez d’expérience pour déterminer quand et / ou si l’automobile est victime de violence?

Ce que je cherche vraiment, c’est un guide des meilleures pratiques sur

  • quand utiliser auto
  • quand il faut l’éviter

Des règles simples qui peuvent être suivies rapidement dans 80% des cas.

En tant que contexte, cette question est déclenchée par ma réponse ici

Je pense que lorsque le type est très connu parmi les co-programmeurs qui travaillent (ou travailleraient) dans votre projet, alors auto peut être utilisé, comme dans le code suivant:

 //good : auto increases readability here for(auto it = v.begin(); it != v.end(); ++it) //v is some [std] container { //.. } 

Ou plus généralement

 //good : auto increases readability here for(auto it = std::begin(v); it != std::end(v); ++it)//v could be array as well { //.. } 

Mais quand le type n’est pas très connu et rarement utilisé, alors je pense que l’ auto semble réduire la lisibilité, comme ici:

 //bad : auto decreases readability here auto obj = ProcessData(someVariables); 

Alors que dans le premier cas, l’utilisation de l’ auto semble très bonne et ne réduit pas la lisibilité, elle peut donc être largement utilisée, mais dans ce dernier cas, elle réduit la lisibilité et ne devrait donc pas être utilisée.


Un autre endroit où auto peut être utilisé est lorsque vous utilisez les new 1 ou make_* , comme ici:

 //without auto. Not that good, looks cumbersome SomeType::SomeOtherType * obj1 = new SomeType::SomeOtherType(); std::shared_ptr obj2 = std::make_shared(args...); std::unique_ptr obj2 = std::make_unique(args...); //With auto. good : auto increases readability here auto obj1 = new SomeType::SomeOtherType(); auto obj2 = std::make_shared(args...); auto obj3 = std::make_unique(args...); 

Ici, c’est très bien, car cela réduit l’utilisation du clavier, sans réduire la lisibilité, car tout le monde peut connaître le type d’ objects créés, simplement en regardant le code.

1. Évitez toutefois d’utiliser des pointeurs new et des pointeurs bruts.


Parfois, le type est tellement hors de propos que la connaissance du type n’est même pas nécessaire, comme dans un modèle d’expression ; en fait, il est pratiquement impossible d’écrire le type (correctement), dans de tels cas, l’ auto est un soulagement pour les programmeurs. J’ai écrit une bibliothèque de modèles d’expression qui peut être utilisée comme:

 foam::composition::expression x; auto s = x * x; //square auto c = x * x * x; //cube for(int i = 0; i < 5 ; i++ ) std::cout << s(i) << ", " << c(i) << std::endl; 

Sortie:

 0, 0 1, 1 4, 8 9, 27 16, 64 

Maintenant, comparez le code ci-dessus avec le code équivalent suivant qui n'utilise pas auto :

 foam::composition::expression x; //scroll horizontally to see the complete type!! foam::composition::expression, foam::composition::expression, foam::operators::multiply>> s = x * x; //square foam::composition::expression, foam::composition::expression, foam::operators::multiply> >, foam::composition::expression, foam::operators::multiply>> c = x * x * x; //cube for(int i = 0; i < 5 ; i++ ) std::cout << s(i) << ", " << c(i) << std::endl; 

Comme vous pouvez le voir, dans de tels cas, la auto rend votre vie plus exponentielle. Les expressions utilisées ci-dessus sont très simples. penser au type de certaines expressions plus complexes:

 auto a = x * x - 4 * x + 4; auto b = x * (x + 10) / ( x * x+ 12 ); auto c = (x ^ 4 + x ^ 3 + x ^ 2 + x + 100 ) / ( x ^ 2 + 10 ); 

Le type de ces expressions serait encore plus énorme et moche, mais grâce à auto , nous pouvons maintenant laisser le compilateur en déduire le type des expressions.


En résumé, le mot-clé auto peut augmenter ou diminuer la clarté et la lisibilité de votre code, en fonction du contexte . Si le contexte indique clairement de quel type il s'agit, ou du moins comment il doit être utilisé (en cas d’iterator de conteneur standard) ou si la connaissance du type réel n’est même pas nécessaire (comme dans les modèles d’expression), alors auto devrait être utilisé , et si le contexte ne le rend pas clair et n'est pas très commun (comme dans le second cas ci-dessus), alors il vaut mieux l' éviter .

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

 for (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.

J’appliquerais la même règle que pour var en C #: utilisez-le libéralement . Cela augmente la lisibilité. A moins que le type d’une variable ne soit suffisamment important pour être explicitement indiqué, dans quels cas cela devrait être fait (duh).

Cependant, je maintiens que (surtout dans les langages statiquement typés) le compilateur est bien meilleur pour nous que pour les types de suivi. La plupart du temps, le type exact n’est pas très important (sinon, les interfaces ne fonctionneraient pas dans la pratique). Il est plus important de savoir quelles opérations sont autorisées. Le contexte devrait nous le dire.

En outre, auto peut effectivement empêcher les bogues , en empêchant les conversions implicites indésirables dans les initialisations. Généralement, l’instruction Foo x = y; effectuera une conversion implicite si y n’est pas de type Foo et qu’une conversion implicite existe. C’est la raison pour laquelle il faut éviter les conversions implicites en premier lieu. Malheureusement, C ++ en a déjà beaucoup trop.

Écrire auto x = y; empêchera ce problème en principe .

D’un autre côté, il devrait être clair que lorsque j’effectue des calculs supposant tel ou tel nombre d’octets dans un entier, le type explicite de la variable doit être connu et doit être clairement indiqué.

Tous les cas ne sont pas aussi clairs mais je maintiens que la plupart le sont, et que

  1. dans la plupart des cas, il est facile de voir si un type explicite doit être connu, et
  2. le besoin de types explicites est relativement rare.

Eric Lippert , développeur principal de l’équipe de compilation C #, a dit beaucoup de choses en ce qui concerne la var .

Je pense que la réponse à votre première question est en quelque sorte non. Nous en soaps assez pour élaborer des directives sur le moment d’utiliser ou d’éviter l’ auto , mais ils laissent encore pas mal de cas où le mieux que nous puissions dire est que nous ne pouvons pas encore donner beaucoup de conseils objectives à leur sujet.

Le cas évident où vous devez presque l’utiliser est dans un modèle lorsque vous voulez (par exemple) le bon type pour contenir le résultat d’une opération sur deux parameters génériques. Dans un cas comme celui-ci, la seule possibilité d’abus ne serait pas vraiment l’abus d’ auto lui auto même, mais le type d’opération générale (ou le type de modèle que vous écrivez, etc.) est-il quelque chose mieux vaut éviter.

Il y a aussi au moins quelques situations où vous devez clairement éviter l’ auto . Si vous utilisez quelque chose comme un type de proxy où vous dépendez de la conversion à partir de proxy-> target pour effectuer une partie du travail en cours, auto (tentera) de créer une cible du même type que la source pour que la conversion n’arrivera pas. Dans certains cas, cela peut simplement retarder la conversion, mais dans d’autres cas, cela ne fonctionnera pas du tout (par exemple, si le type de proxy ne prend pas en charge l’affectation, ce qui est souvent le cas).

Un autre exemple serait quand vous devez vous assurer qu’une variable particulière a un type spécifique pour quelque chose comme une interface externe. Par exemple, envisagez d’appliquer le masque de réseau à une adresse IP (v4). Par souci d’argument, supposons que vous travaillez avec les octets individuels de l’adresse (par exemple, en représentant chacun comme un caractère unsigned char ), nous nous retrouvons donc avec quelque chose comme octets[0] & mask[0] . Grâce aux règles de promotion des types de C, même si les deux opérandes sont des caractères unsigned char , le résultat va généralement être int . Cependant, le résultat doit être un caractère unsigned char (c.-à-d. Un octet) et non un int (généralement 4 octets). En tant que tel, dans cette situation, l’ auto serait presque certainement inappropriée.

Cela laisse encore beaucoup de cas où c’est un appel de jugement si. Ma propre tendance dans ces cas est de traiter auto comme valeur par défaut et d’utiliser uniquement un type explicite dans les cas qui sont au moins un peu comme ce dernier cas, même si un type particulier n’est pas nécessaire pour corriger opération que je veux vraiment un type particulier, même si cela peut impliquer une conversion implicite.

Ma conjecture (mais c’est juste une supposition) est que, avec le temps, je vais probablement tendre encore plus dans cette direction. Comme je suis de plus en plus habitué au choix des types de compilateurs, je trouverai que bon nombre de cas où je pense actuellement que je devrais spécifier le type, je n’en ai pas vraiment besoin et le code sera parfait.

Je soupçonne que beaucoup d’entre nous (et les plus âgés / plus expérimentés que nous sums, probablement les pires, nous en parlerons) utiliseront des types explicites pour des raisons de performance, et que notre choix améliorera les performances. . Une partie du temps, nous avons peut -être même raison – mais comme la plupart d’entre nous l’ont déjà dit, nos suppositions sont souvent erronées (surtout quand elles reposent sur des hypothèses implicites), et les compilateurs et les transformateurs s’améliorent généralement. au fil du temps aussi bien.

J’ai utilisé des langues avec une inférence complète. Je ne vois aucune raison de ne pas mettre l’ auto partout où c’est techniquement possible *. En fait, j’ai peut-être déjà écrit auto i = 0; , où int est un caractère plus court que auto . Je ne suis même pas sûr de l’avoir fait parce que le fond est le suivant: je ne me soucie pas de la dactylographie.

*: par exemple, auto int[] = { 0, 1, 2, 3 } ne fonctionne pas.

Utilisez-le uniquement avec des types répétitifs longs tels que les modèles longs et les types de fonctions lambda. Essayez de l’éviter si vous le pouvez pour clarifier les choses.