web-dev-qa-db-fra.com

Le nouveau mot clé "auto"; Quand faut-il l'utiliser pour déclarer un type de variable?

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

Avons-nous (en tant que communauté) suffisamment d'expérience pour déterminer quand et/ou si l'automobile est maltraité?

Ce que je recherche vraiment, c’est un guide de bonnes pratiques

  • quand utiliser auto
  • quand il faut éviter

De simples règles empiriques pouvant être suivies rapidement dans 80% des cas.

En tant que contexte, cette question découle de ma réponse ici

84
Martin York

Je pense que lorsque le type est très connu des 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 utilisé rarement, alors je pense que auto semble réduire la lisibilité, comme ici:

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

Tandis que dans le premier cas, l'utilisation de auto semble très bonne et ne réduit pas la lisibilité, elle peut donc être utilisée de manière extensive, mais dans le second 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 new1 ou make_* _ fonctions, comme ici:

//without auto. Not that good, looks cumbersome
SomeType<OtherType>::SomeOtherType * obj1 = new SomeType<OtherType>::SomeOtherType();
std::shared_ptr<XyzType> obj2 = std::make_shared<XyzType>(args...);
std::unique_ptr<XyzType> obj2 = std::make_unique<XyzType>(args...);

//With auto. good : auto increases readability here
auto obj1 = new SomeType<OtherType>::SomeOtherType();
auto obj2 = std::make_shared<XyzType>(args...);
auto obj3 = std::make_unique<XyzType>(args...);

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

1. Évitez cependant d'utiliser new et les pointeurs bruts.


Parfois, le type est tellement peu pertinent que la connaissance du type n'est même pas nécessaire, comme dans modèle d'expression; en fait, pratiquement il est impossible d'écrire le type (correctement), dans ce cas, 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<int> 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<int> x;

//scroll horizontally to see the complete type!!
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply>> s = x * x; //square
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply> >, foam::composition::expression<int>, 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 constater, dans de tels cas, auto vous rend la vie plus facile de façon exponentielle. Les expressions utilisées ci-dessus sont très simples. réfléchissez au type d'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 telles expressions serait encore plus énorme et moche, mais grâce à auto, nous pouvons maintenant laisser le compilateur déduire le type des expressions.


La conclusion est donc: le mot clé auto pourrait augmenter ou diminuer la clarté et la lisibilité de votre code, selon le contexte. Si le contexte indique clairement ce que type c'est, ou du moins comment il devrait être utilisé (dans le cas d'un itérateur de conteneur standard) ou la connaissance du type réel n'est même pas nécessaire (comme dans l'expression templates), alors auto devrait être tilisé, et si le contexte ne le dit pas clairement et n’est pas très courant (comme le deuxième cas ci-dessus), il devrait alors être mieux être évité.

142
Nawaz

Facile. Utilisez-le quand vous peu importe quel est le type. Par exemple

for (auto i : some_container) {
   ...

Tout ce qui m'importe ici est que i correspond à tout ce qui se trouve dans le conteneur.

C'est un peu comme typedefs.

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

Ici, je me moque de savoir si h et w sont des flottants ou des doubles, mais qu’ils sont quel que soit le type approprié pour exprimer les hauteurs et les poids .

Ou considérer

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

Ici, tout ce qui m'importe, c’est que c’est un itérateur approprié, supportant operator++(), c’est un peu comme une frappe de canard à cet égard.

De plus, le type de lambda ne peut pas être épelé, donc auto f = []... Est un bon style. L’alternative est de lancer vers std::function Mais cela vient avec des frais généraux.

Je ne peux pas vraiment concevoir un "abus" de auto. Le plus proche que je puisse imaginer est de vous priver d’une conversion explicite en un type significatif - mais vous n’utiliseriez pas auto, vous construiriez un objet du type souhaité.

Si vous pouvez supprimer une redondance dans votre code sans introduire d’effets secondaires, alors il doit être bon de le faire.

16
spraff

J'appliquerais la même règle que pour var en C #: utilisez-le généreusement . Il augmente lisibilité. À moins que le type d'une variable soit suffisamment important pour être déclaré explicitement, dans quels cas cela devrait être fait (duh).

Néanmoins, je maintiens que (en particulier dans les langages à typage statique) le compilateur est bien meilleur que nous pour le suivi des types. La plupart du temps, le type exact n’est de toute façon 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 dire ça.

De plus, auto peut réellement empêcher les bugs, en empêchant les conversions implicites non désirées lors des initialisations. En général, la déclaration 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 est préférable d'éviter les conversions implicites. Malheureusement, C++ en a déjà beaucoup trop.

L'écriture auto x = y; _ empêchera ce problème en principe.

D’autre part, il devrait être clair que, lorsque j’effectue des calculs qui supposent tel ou tel nombre d’octets dans un entier, le type explicite de la variable doit être connu et 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 comparativement rare.

Eric Lippert , développeur principal de l'équipe du compilateur C #, a déclaré à peu près la même chose en ce qui concerne var .

14
Konrad Rudolph

Je pense que la réponse à votre première question est en quelque sorte non. Nous en savons assez pour établir quelques directives sur le moment d'utiliser ou d'éviter auto, mais il en reste encore quelques cas où le mieux que nous puissions dire actuellement est que nous ne pouvons pas encore donner beaucoup d'objectifs. conseils à leur sujet.

Le cas évident où vous avez presque à utiliser est dans un modèle lorsque vous souhaitez (par exemple) le type approprié pour conserver le résultat d'une opération sur deux paramètres génériques. Dans un cas comme celui-ci, la seule possibilité d'abus ne serait pas vraiment l'abus de auto, mais plutôt le type d'opération que vous faites (ou le type de modèle que vous écrivez, etc.) C’est quelque chose que vous feriez mieux d’éviter.

Il existe également au moins quelques situations où vous devez clairement éviter auto. Si vous utilisez quelque chose comme un type de proxy où vous comptez sur la conversion de proxy-> target pour effectuer une partie du travail à effectuer, auto créera (tentera de) créer une cible du même type. en tant que source pour que la conversion ne se produise pas. Dans certains cas, cela peut simplement retarder la conversion, mais dans d’autres, 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 lorsque vous devez vous assurer qu'une variable particulière a un type spécifique, par exemple pour une interface externe. Par exemple, envisagez d'appliquer le masque de réseau à une adresse IP (v4). Par souci d'argumentation, supposons que vous travaillez avec les octets individuels de l'adresse (par exemple, en les représentant chacun comme un unsigned char), on se retrouve donc avec quelque chose comme octets[0] & mask[0]. Merci aux règles de promotion de type de C, même si les deux opérandes sont unsigned chars, le résultat sera généralement int. Nous avons besoin pour que le résultat soit un unsigned char bien que (c'est-à-dire un octet) pas un int (typiquement 4 octets) cependant. En tant que tel, dans cette situation, auto serait presque certainement inapproprié.

Cela laisse encore beaucoup de cas où c'est un jugement qui appelle. Ma propre tendance pour ces cas est de traiter auto comme valeur par défaut et d’utiliser un type explicite uniquement dans les cas où peu comme le dernier cas que j'ai cité ci-dessus - même si un type particulier n'est pas nécessaire pour un fonctionnement correct que je vraiment veut un type particulier, même si cela peut impliquer une conversion implicite.

Mon hypothèse (mais c'est juste une hypothèse) est qu'avec le temps, je vais probablement encore plus dans cette direction. Au fur et à mesure que je me familiariserai avec les types de sélection du compilateur, je constaterai qu’un bon nombre de cas où je pense actuellement devrais spécifier le type, Je n'en ai vraiment pas besoin et le code ira très bien.

Je pense que beaucoup d'entre nous (et les plus âgés/plus expérimentés que nous sommes, probablement les pires à ce sujet) utiliserons des types explicites pour des raisons qui finalement remontent à un sentiment de performance et croient que notre choix améliorera les performances . Une partie du temps, nous pouvons même avoir raison - mais comme la plupart d’entre nous avec cette expérience ont constaté, nos suppositions sont souvent fausses (surtout lorsqu’ils ont sont basés sur des suppositions implicites), et les compilateurs et les processeurs s’améliorent généralement dans de telles situations au fil du temps.

4
Jerry Coffin

J'ai utilisé des langues avec une inférence complète. Je ne vois aucune raison de ne pas mettre 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 car le fond de la phrase est le suivant: je n'aime pas le typage de manifeste.

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

2
Luc Danton

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

2
Will03uk