web-dev-qa-db-fra.com

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

J'utilise le nouveau mot clé auto disponible dans la norme C++ 11 pour les types compliqués basés sur des modèles, ce à quoi je pense qu'il a été conçu. Mais je l'utilise aussi pour des choses comme:

auto foo = std::make_shared<Foo>();

Et plus sceptique pour:

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

Je n'ai pas beaucoup discuté de ce sujet. Il semble que auto pourrait être utilisé de manière excessive, puisqu'un type est souvent une forme de documentation et de contrôle de cohérence. 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 est réalisé dans la pratique.

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

200
Alan Turing

Je pense qu'il faut utiliser le mot 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 s'agit de int. Vous ne pouvez pas simplement écrire int car cela pourrait être changé dans le futur. Je voudrais écrire auto dans ce cas.

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

Voici quelques exemples:

auto foo = std::make_shared<Foo>(); // 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<some_class> 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)
118

Utilisez auto partout où vous le pouvez, en particulier const auto pour que les effets secondaires soient moins préoccupants. Vous n'aurez pas à vous soucier des types, sauf dans les cas les plus évidents, mais ils seront toujours vérifiés statiquement pour vous, et vous pourrez éviter certaines répétitions. Lorsque auto n'est pas faisable, vous pouvez utiliser decltype pour exprimer les types sémantiquement sous la forme contracts en fonction d'expressions. Votre code sera différent, mais ce sera un changement positif.

54
Jon Purdy

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

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

Tout ce qui m'importe ici est que i est ce qu'il y a 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 seulement quel que soit le type approprié pour exprimer des hauteurs et des 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é, qui supporte operator++(), c’est un peu comme si vous tapiez un canard à cet égard.

De plus, le type de lambda ne peut pas être épelé, donc auto f = []... est un bon style. L’alternative consiste à lancer 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 pour cela, vous construiriez un objet du type souhaité.

Si vous pouvez supprimez une partie de la redondance dans votre code sans introduire d'effets secondaires, alors il = doit soyez bon de le faire.

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

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

Ici nous nous soucions du type, alors nous devrions écrire Someclass i; et for(unsigned x = y;...

46
spraff

Fonce. Utilisez auto partout où cela facilite la rédaction de code.

Au moins certains types de programmeurs vont au-delà de l’utilisation abusive de chaque nouvelle fonctionnalité, quelle que soit sa langue. Ce n’est que par une surutilisation modérée par certains programmeurs expérimentés (et non par des noobs) que le reste des programmeurs expérimentés apprennent les limites d’un bon usage. Une surutilisation extrême est généralement mauvaise, mais peut s'avérer utile car une telle surutilisation peut entraîner 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();

lorsque le type est indiqué zéro fois, il se peut que je veuille modifier ces lignes pour inclure des types. Le premier exemple est excellent, car le type est indiqué une fois et auto nous évite d'avoir à écrire deux fois des types désordonnés. Hourra pour C++++. Mais montrer explicitement le caractère zéro fois, s'il n'est pas facilement visible dans une ligne proche, me rend nerveux, du moins en C++ et ses successeurs immédiats. Pour d'autres langages conçus pour fonctionner à un niveau supérieur avec plus d'abstraction, de polymorphisme et de généricité, c'est bon.

43
DarenW

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

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

auto ne nuit pas à la lisibilité.

Un autre exemple est le type de règle de l'analyseur, qui peut être long et compliqué. Comparer:

   auto spaces = space & space & space;

avec

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

Par contre, quand le type est connu et qu’il est simple, c’est bien mieux s’il indique explicitement:

int i = foo();

plutôt que

auto i = foo();
36
Gene Bushuyev

À C++ et au-delà de 2012 dans le panneau Ask Us Anything, il y a eu un { échange fantastique) entre Andrei Alexandrescu, Scott Meyers et Herb Sutter sur le moment d'utiliser auto . Passez à la minute 25:03 pour une discussion de 4 minutes. Les trois orateurs ont donné d’excellents arguments à ne pas oublier pour savoir quand utiliser pasauto.

J'encourage fortement les gens à arriver à leurs propres conclusions, mais ce que je retenais était de utiliser auto partout sauf si:

  1. Ça fait mal la lisibilité
  2. La conversion automatique des types (par exemple des constructeurs, des affectations, des types intermédiaires de gabarits, une conversion implicite entre des largeurs entières) est un sujet de préoccupation.

L'utilisation par les libéraux de explicit aide à réduire les inquiétudes pour le dernier, ce qui permet de réduire le temps pendant lequel le premier est un problème.

En reformulant ce que Herb a dit, "si vous ne faites pas X, Y et Z, utilisez auto. Apprenez ce que sont X, Y et Z et sortez et utilisez auto partout ailleurs."

34
Sean

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

auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.

Les bogues causés par ce type d’erreurs sont un problème majeur pour le débogage. Une solution possible consiste à convertir explicitement le résultat en type attendu si vous souhaitez absolument utiliser auto pour le style de déclaration de gauche à droite.

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

J'utilise auto sans restriction et je n'ai rencontré aucun problème. Je finis même parfois par l’utiliser pour des types simples comme int. Cela fait de 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 exemple,.

auto i = MyClass();

au lieu de

MyClass i;

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

Souvent, le type exact d'objet ne me dérange pas, je m'intéresse davantage à sa fonctionnalité et, comme les noms de fonction disent généralement quelque chose à propos des objets qu'ils renvoient, auto ne fait pas mal: dans, par exemple. auto s = mycollection.size(), je suppose que s sera une sorte d’entier, et dans les rares cas où je me soucie du type exact, vérifions le prototype de fonction alors (je veux dire, je préfère avoir à vérifier quand j’ai besoin de l’information, plutôt qu’à priori lors de l’écriture du code, juste au cas où il serait utile un jour, comme dans int_type s = mycollection.size()).

En ce qui concerne 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 say 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 utiliser, je ne suis pas compétent pour donner des conseils!

9
rafak

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 réellement une référence. Dans ce cas, il s'agit de MyBigObject au lieu de MyBigObject &. Vous finissez par copier un gros objet sans vous en rendre compte.

Si vous obtenez une référence directement d'une méthode, vous ne penserez peut-être pas à sa nature.

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();
2
user2495422

L’un des majeurs problèmes avec le programme C++ est qu’il vous permet d’utiliser la variable non initialisée. Cela nous conduit à un comportement de programme non déterministe. Il convient de noter que le compilateur moderne envoie maintenant des messages d’avertissement/message appropriés si le programme est fatigué de l’utiliser.

Juste pour illustrer cela, considérons ci-dessous le programme c ++:

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

Si je compile 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 [-Wuninitialisé]

y + = x;

    ^

=============================================== ============================= Maintenant, si nous changeons notre programme qui utilise auto, puis compilons nous obtenons 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 auto

Ce concept et d'autres très bons concepts modernes du C++ sont expliqués par un expert en C++ Herb Shutter dans son CppCon14 talk:

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

2
Mantosh Kumar

Le mot clé auto ne peut être utilisé que pour une variable locale, et non pour des arguments ou des membres de classe/structure. Il est donc 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 pendant le débogage, la variable sizeof le signale correctement, la variable decltype donne le type correct - il n'y a pas de problème. Je ne compte pas auto comme surutilisé, jamais!

1
Ajay

Utilisez auto où il est logique de déduire un type. Si vous savez que quelque chose est un entier, ou si vous savez que c'est une chaîne, utilisez simplement int/std :: string, etc. ou obscurcit le code.

C'est mon avis quand même.

1
LainIwakura

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

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

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

Mais je dirais que c'est trop restrictif. Parfois, je ne me soucie pas des types, car la déclaration est suffisamment informative sans que je prenne la peine de prendre le temps de comprendre le type. Qu'est-ce que je veux dire par là? Prenons l'exemple qui est apparu dans certaines des réponses:

auto x = f();

Qu'est-ce qui en fait un exemple d'utilisation abusive de auto? Est-ce que je ne connais pas le type de retour de f()? Eh bien, cela peut effectivement aider si je le savais, mais ce n’est pas ma préoccupation principale. Ce qui est beaucoup plus un problème, c'est que x et f() n'ont pas beaucoup de sens. Si nous avions:

auto nugget = mine_gold();

au lieu de cela, alors je me fiche généralement de savoir 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 ressentir le besoin de connaître également son type.

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

  • Vous estimez que le nom de la variable, associé à l'expression d'initialisation/d'affectation, ne fournit pas suffisamment d'informations sur le fonctionnement de l'instruction.
  • Vous estimez que le nom de la variable, associé à l'expression d'initialisation/d'affectation, fournit des informations "trompeuses" sur le type à utiliser - c.-à-d. Si vous deviez deviner ce qui se produisait à la place de l'automobile, vous seriez capable de deviner - et ce serait faux, et cette fausse hypothèse a des répercussions plus tard dans le code.
  • Vous souhaitez 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.
0
einpoklum