Y at-il un inconvénient à déclarer des variables avec auto en C ++?

Il semble que auto était une fonctionnalité assez importante à append en C ++ 11, qui semble suivre beaucoup de nouvelles langues. Comme avec un langage comme Python, je n’ai pas vu de déclaration de variable explicite (je ne suis pas sûr que cela soit possible en utilisant les standards Python).

Y a-t-il un inconvénient à utiliser auto pour déclarer des variables au lieu de les déclarer explicitement?

Vous avez seulement posé des questions sur les inconvénients, alors je souligne certains d’entre eux. Bien utilisée, l’ auto présente également plusieurs avantages. Les inconvénients résultent de la facilité d’utilisation et de la possibilité accrue que le code se comporte de manière involontaire.

Le principal inconvénient est que, en utilisant auto , vous ne connaissez pas nécessairement le type d’object créé. Il y a aussi des occasions où le programmeur peut s’attendre à ce que le compilateur en déduise un type, mais le compilateur en déduit complètement un autre.

Donné une déclaration comme

 auto result = CallSomeFunction(x,y,z); 

vous n’avez pas nécessairement connaissance du type de result . Ce pourrait être un int . Ce pourrait être un pointeur. Ce pourrait être autre chose. Tous ceux-ci prennent en charge différentes opérations. Vous pouvez également changer radicalement le code par un changement mineur comme

 auto result = CallSomeFunction(a,y,z); 

car, en fonction des surcharges existantes pour CallSomeFunction() le type de résultat peut être complètement différent et le code suivant peut donc se comporter de manière complètement différente de celle prévue. Vous pourriez déclencher soudainement des messages d’erreur dans un code ultérieur (par exemple, en essayant par la suite de déréférencer un int , en essayant de changer quelque chose qui est maintenant const ). Le changement le plus sinistre est celui où votre changement passe au-delà du compilateur, mais le code suivant se comporte de manière différente et inconnue, voire boguée.

Ne pas avoir une connaissance explicite du type de certaines variables rend donc plus difficile la justification rigoureuse de l’affirmation selon laquelle le code fonctionne comme prévu. Cela signifie plus d’efforts pour justifier les revendications de “adéquation” dans les domaines hautement critiques (par exemple, critiques pour la sécurité ou critiques).

L’autre inconvénient, plus commun, est la tentation pour un programmeur d’utiliser auto comme un instrument contondant pour forcer le code à se comstackr, plutôt que de penser à ce que fait le code et de travailler pour le faire correctement.

Ce n’est pas un inconvénient de l’ auto d’une manière fondée sur auto principes, mais en termes pratiques, cela semble être un problème pour certains. Fondamentalement, certaines personnes: a) traitent l’ auto comme un sauveur pour les types et coupent leur cerveau quand on l’utilise, ou b) oublient que auto déduit toujours aux types de valeur. Cela amène les gens à faire des choses comme ceci:

 auto x = my_obj.method_that_returns_reference(); 

Oups, nous venons de copier un object. C’est souvent soit un bug, soit un échec de performance. Ensuite, vous pouvez aussi basculer dans l’autre sens:

 const auto& stuff = *func_that_returns_unique_ptr(); 

Maintenant, vous obtenez une référence pendante. Ces problèmes ne sont pas causés par l’ auto , donc je ne les considère pas comme des arguments légitimes. Mais il semble que auto rend ces problèmes plus courants (d’après mon expérience personnelle), pour les raisons que j’ai énumérées au début.

Je pense que, compte tenu du temps, les gens vont s’ajuster et comprendre la division du travail: l’ auto déduit le type sous-jacent, mais vous voulez toujours penser à la référence et à la constance. Mais ça prend un peu de temps.

D’autres réponses mentionnent des inconvénients tels que “vous ne savez pas vraiment quel est le type d’une variable”. Je dirais que ceci est en grande partie lié à la convention de nommage bâclée dans le code. Si vos interfaces sont clairement nommées, vous ne devriez pas avoir à vous soucier du type exact. Bien sûr, auto result = callSomeFunction(a, b); ne vous dit pas grand chose. Mais auto valid = isValid(xmlFile, schema); vous dit assez pour utiliser valid sans avoir à vous soucier de son type exact. Après tout, avec juste if (callSomeFunction(a, b)) , vous ne connaissez pas non plus le type. La même chose avec tout autre object temporaire de sous-expression. Donc, je ne considère pas cela comme un réel inconvénient de l’ auto .

Je dirais que son principal inconvénient est que parfois, le type de retour exact n’est pas ce que vous voulez travailler. En effet, le type de retour réel diffère parfois du type de retour “logique” en tant que détail d’implémentation / d’optimisation. Les modèles d’expression sont un excellent exemple. Disons que nous avons ceci:

 SomeType operator* (const Masortingx &lhs, const Vector &rhs); 

Logiquement, on s’attendrait à ce que SomeType soit Vector , et nous voulons absolument le traiter comme tel dans notre code. Cependant, il est possible que, pour des raisons d’optimisation, la bibliothèque d’algèbre que nous utilisons implémente des modèles d’expression, et le type de retour réel est le suivant:

 MultExpression operator* (const Masortingx &lhs, const Vector &rhs); 

Maintenant, le problème est que MultExpression stockera vraisemblablement une const Masortingx& et const Vector& interne; il s’attend à ce qu’il soit converti en un Vector avant la fin de sa pleine expression. Si nous avons ce code, tout va bien:

 extern Masortingx a, b, c; extern Vector v; void compute() { Vector res = a * (b * (c * v)); // do something with res } 

Cependant, si nous avions utilisé la auto ici, nous pourrions avoir des problèmes:

 void compute() { auto res = a * (b * (c * v)); // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist } 

L’un des inconvénients est que parfois vous ne pouvez pas déclarer const_iterator avec auto . Vous obtiendrez un iterator ordinaire (non const) dans cet exemple de code tiré de cette question :

 map usa; //...init usa auto city_it = usa.find("New York"); 

Cela rend votre code un peu plus difficile à lire. Imaginez quelque chose comme ça:

 auto output = doSomethingWithData(variables); 

Maintenant, pour déterminer le type de sortie, vous devrez suivre la signature de la fonction doSomethingWithData .

Comme ce développeur, je déteste l’ auto . Ou plutôt, je déteste la mauvaise utilisation de l’ auto .

Je suis de l’avis (fort) que l’ auto est pour vous aider à écrire du code générique, pas pour réduire la saisie .
C ++ est un langage dont le but est de vous permettre d’écrire du code robuste, et non de minimiser le temps de développement.
Ceci est assez évident à partir de nombreuses fonctionnalités de C ++, mais malheureusement, certaines des plus récentes comme auto qui réduisent la saisie induisent les gens en erreur en leur faisant croire qu’ils devraient commencer à être paresseux en tapant.

Dans les jours précédant l’ auto , les utilisateurs utilisaient typedef s, ce qui était très bien car typedef permettait au concepteur de la bibliothèque de vous aider à déterminer le type de retour, afin que leur bibliothèque fonctionne comme prévu. Lorsque vous utilisez auto , vous éliminez ce contrôle du concepteur de la classe et demandez au compilateur de déterminer le type, ce qui supprime l’un des outils C ++ les plus puissants de la boîte à outils et risque de casser leur code.

Généralement, si vous utilisez auto , cela devrait être parce que votre code fonctionne pour n’importe quel type raisonnable , pas parce que vous êtes trop paresseux pour écrire le type avec lequel il devrait fonctionner. Si vous utilisez auto comme outil d’aide à la paresse, il se peut que vous commenciez à introduire des bogues subtils dans votre programme, généralement causés par des conversions implicites qui ne se sont pas produites car vous utilisiez auto .

Malheureusement, ces bogues sont difficiles à illustrer dans un court exemple, car leur brièveté les rend moins convaincants que les exemples réels apparaissant dans un projet utilisateur – cependant, ils se produisent facilement dans un code lourd qui attend certaines conversions implicites. endroit.

Si vous voulez un exemple, il y en a un ici . Une petite remarque cependant: avant d’être tenté de critiquer le code: gardez à l’esprit que de nombreuses bibliothèques bien connues et matures ont été développées autour de ces conversions implicites, et qu’elles sont là pour résoudre des problèmes difficiles voire impossibles. pour résoudre autrement. Essayez de trouver une meilleure solution avant de les critiquer.

auto n’a pas d’inconvénients en soi , et je vous conseille de l’utiliser (de façon manuelle) partout dans le nouveau code. Cela permet à votre code de vérifier systématiquement les caractères et d’éviter systématiquement les coupures silencieuses. (Si B dérive de A et une fonction renvoyant A retourne soudainement B , alors auto se comporte comme prévu pour stocker sa valeur de retour)

Bien que le code pré-C ++ 11 puisse s’appuyer sur des conversions implicites induites par l’utilisation de variables explicitement typées. Changer une variable explicitement typée en auto peut changer le comportement du code , il vaut donc mieux être prudent.

Le mot auto clé auto déduit simplement le type de la valeur de retour. Par conséquent, il n’est pas équivalent à un object Python, par exemple

 # Python a a = 10 # OK a = "10" # OK a = ClassA() # OK // C++ auto a; // Unable to deduce variable a auto a = 10; // OK a = "10"; // Value of const char* can't be assigned to int a = ClassA{} // Value of ClassA can't be assigned to int a = 10.0; // OK, implicit casting warning 

Comme l’ auto est déduite lors de la compilation, elle n’aura aucun inconvénient à l’exécution.

Ce que personne n’a mentionné ici jusqu’ici, mais pour lui-même vaut une réponse si vous me le demandez.

Puisque (même si tout le monde devrait savoir que le code C != C++ ) écrit en C peut facilement être conçu pour fournir une base pour du code C ++ et être conçu sans trop d’efforts pour être compatible C ++, cela pourrait être une nécessité pour la conception.

Je connais certaines règles où certaines constructions bien définies de C sont pas valides pour C++ et vice versa. Mais cela se traduirait simplement par des exécutables brisés et la clause UB connue s’applique, la plupart du temps par des boucles étranges entraînant des plantages ou autres (ou même ne pas être détectés, mais peu importe ici).

Mais auto c’est la première fois que cela change!

Imaginez que vous auto comme spécificateur de classe de stockage auto avant de transférer le code. Ce ne serait même pas forcément (selon la manière dont il a été utilisé) “break”; en fait, cela pourrait changer silencieusement le comportement du programme.

C’est quelque chose qu’il faut garder à l’esprit.


1 Au moins la première fois que je suis au courant.

Comme je l’ai décrit dans cette réponse, l’ auto peut parfois entraîner des situations géniales que vous n’aviez pas l’intention. Vous devez dire explicitement que auto& pour avoir un type de référence en faisant simplement auto peut créer un type de pointeur. Cela peut entraîner une confusion en omettant le spécificateur tous ensemble, ce qui entraîne une copie de la référence au lieu d’une référence réelle.

L’une des raisons pour lesquelles je peux penser est que vous perdez la possibilité de contraindre la classe renvoyée. Si votre fonction ou méthode a renvoyé un long bit 64, et que vous ne vouliez qu’un int 32 non signé, vous perdez la possibilité de contrôler cela.

Un autre exemple irritant:

 for (auto i = 0; i < s.size(); ++i) 

génère un avertissement ( comparison between signed and unsigned integer expressions [-Wsign-compare] ), car i est un int signé. Pour éviter cela, vous devez écrire par exemple

 for (auto i = 0U; i < s.size(); ++i) 

ou peut-être mieux:

 for (auto i = 0ULL; i < s.size(); ++i) 

Je pense que auto est bon lorsqu’il est utilisé dans un contexte localisé, où le lecteur peut facilement et facilement déduire son type, ou bien documenté avec un commentaire de ce type ou un nom qui en déduit le type réel. Ceux qui ne comprennent pas comment cela fonctionne peuvent se tromper, comme l’utiliser au lieu de template ou similaire. Voici quelques bons et mauvais cas d’utilisation à mon avis.

 void test (const int & a) { // b is not const // b is not a reference auto b = a; // b type is decided by the comstackr based on value of a // a is int } 

Bonnes utilisations

Les iterators

 std::vector,int> v(); .. std::vector,int>::iterator it = v.begin(); // VS auto vi = v.begin(); 

Pointeurs de fonction

 int test (ClassWithLongName1 a, ClassWithLongName2 b, int c) { .. } .. int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test; // VS auto *f = test; 

Mauvaises utilisations

Flux de données

 auto input = ""; .. auto output = test(input); 

Signature de fonction

 auto test (auto a, auto b, auto c) { .. } 

Cas sortingviaux

 for(auto i = 0; i < 100; i++) { .. }