Est-il un inconvénient à ce que déclarer des variables avec l'auto en C++?
Il semble que auto
était une assez bonne fonctionnalité à ajouter dans C++11 qui semble suivre beaucoup de nouveaux langages. Comme avec un langage comme Python, je n'ai vu aucune déclaration explicite des variables (je ne suis pas sûr si c'est possible à l'aide de Python normes).
Est-il un inconvénient à l'utilisation de auto
de déclarer les variables au lieu de déclarer explicitement eux?
- Voir aussi: stackoverflow.com/questions/6434971/..., stackoverflow.com/questions/15254461/..., stackoverflow.com/questions/6900459/..., stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous, et peut-être d'autres. Vous ne savez pas si ce sont des doublons exacts, mais ils sont certainement les candidats.
- Le seul inconvénient que j'ai trouvé était quand je l'ai porté une base de code pour un (console) de la plateforme dont le compilateur n'était pas (et n'avait aucune intention de soutenir l') C++11 caractéristiques!
- Juste pour être complet, GotW #94 "Presque Toujours Auto": herbsutter.com/2013/08/12/...
- J'écoutais cppcast et il était fait mention de l'affrontement b/w auto et de la liste des initialiseurs. Je vais essayer de trouver ce podcast.
- le premier inconvénient que je pense a un impact sur la lisibilité du code
- Vous devez les accepter une réponse DxAlpha.
Vous devez vous connecter pour publier un commentaire.
Vous avez des questions sur les inconvénients, je suis donc de mettre en lumière certaines de celles-ci. Lorsque bien utilisé,
auto
a plusieurs avantages. Les inconvénients raison de la facilité d'abus, et de l'augmentation du potentiel de code à se comporter de manière non intentionnelle.Le principal inconvénient est que, en utilisant
auto
, vous ne savent pas forcément le type de l'objet créé. Il y a aussi des occasions où le programmeur pourrait s'attendre à ce que le compilateur pour en déduire un type, mais le compilateur catégoriquement en déduit une autre.Donné une déclaration comme
vous n'avez pas nécessairement besoin de la connaissance de ce type de
result
est. Il pourrait être unint
. Il pourrait être un pointeur. Il pourrait être quelque chose d'autre. Tous ceux qui prennent en charge différentes opérations. Vous pouvez également changer de façon radicale le code par un changement mineur commeparce que, selon ce que les surcharges existent pour
CallSomeFunction()
le type de résultat peut être complètement différent - et suivantes code peut donc se comporter d'une manière complètement différente que prévu. Vous pourriez soudainement déclencher des messages d'erreur plus tard dans le code(par exemple, par la suite essayer de déréférencer unint
, d'essayer de changer quelque chose qui est maintenantconst
). Le plus sinistre, le changement est là que votre changement de voiles passé le compilateur, mais à la suite de code se comporte dans différentes et inconnues - éventuellement buggy - façons.De ne pas avoir les connaissances explicites du type de certaines variables par conséquent, il est plus difficile de justifier rigoureusement une revendication que le code fonctionne comme prévu. Cela signifie plus d'efforts pour justifier les allégations de "fit for purpose" en haute-criticité (par exemple essentiels à la sécurité ou à la mission-critique) domaines.
Les autres, les plus courants inconvénient, c'est la tentation pour un programmeur d'utiliser
auto
comme un instrument contondant, à force de code à compiler, plutôt que de penser à ce que fait le code, et de travail pour obtenir ce droit.auto
, alors la plupart de canard tapé langue subir de tels inconvénient par conception de!CallSomeFunction()
retourne un type différent selon la séquence de ses arguments, c'est un défaut de conception deCallSomeFunction()
, pas un problème deauto
. Si vous n'avez pas lu la documentation d'une fonction que vous utilisez avant de l'utiliser, c'est un des défauts de la programmeur, pas un problème deauto
. - Mais je comprends que vous êtes jouer l'avocat du diable ici, c'est juste que Nir Friedman a la meilleure affaire.T CallSomeFunction(T, int, int)
être un défaut de conception? Évidemment, il "retourne un type différent selon la séquence de ses arguments."int
s'. 😉 Mais je vois d'où vous venez, et de reporter à mon deuxième point: Ne pas utiliser une fonction sans avoir lu sa documentation. 😉auto
est une fonction - comme beaucoup d'lorsque mal utilisés - qui permet à un programmeur pour obtenir le code défectueux passé un compilateur. Je traite potentiel de mésusage comme un inconvénient.auto
, vous ne savent pas forcément le type de l'objet créé." Pouvez-vous expliquer pourquoi c'est un problème avecauto
, et non pas un problème avec les sous-expression temporaires? Pourquoi est -auto result = foo();
mauvais, maisfoo().bar()
pas?explicit
, ou une autre expression est définie dans une sémantique de manière incohérente. En bref, quelque chose de suspect et vous feriez mieux de savoir quoi.auto
partout dans le nouveau code, mais être prudent lors de la modification d'un mot explicite de la déclaration de type deauto
dans le code de legs.int
ne peut pas être uniforme-initialisé à partir d'un flotteur.auto
fanboy mais c'est un inconvénient sérieux, quand les gens écrivent partout sans réellement de la pensée.int
peut être initialisé à partir d'unfloat
. La Norme ne prévoit qu'un diagnostic est émis lorsque le rétrécissement peut se produire lors de l'utilisation de l'accolade de l'initialisation. Il n'empêche pas nécessairement les compilateurs de la compilation du code.g++
, par exemple, émet juste un avertissement... sans doute causer furieux débat entre ceux qui veulent une erreur de disque dur et ceux qui ne comprennent pas l'uniforme de l'initialisation et veux pas d'avertissement. modifier ah oui: gcc.gnu.org/bugzilla/show_bug.cgi?id=55783Ce n'est pas un inconvénient de
auto
dans un moyen de principes exactement, mais dans la pratique, il semble être un problème pour certains. Fondamentalement, certaines personnes soit: a) traiterauto
comme un sauveur pour les types et les fermer leur cerveau lors de l'utilisation, ou b) oublier queauto
toujours en déduit la valeur de types. Ce qui pousse les gens à faire des choses comme ça:Oups, nous avons, d'une profondeur copié un objet. C'est souvent soit un bug ou d'une performance de l'échec. Ensuite, vous pouvez balancer dans l'autre sens aussi:
Maintenant, vous obtenez une balançant de référence. Ces problèmes ne sont pas causés par
auto
à tous, donc je ne considère pas leur bien-fondé des arguments contre elle. Mais il sembleauto
rend ces problème plus commun (à partir de mon expérience personnelle), pour les raisons que j'ai mentionnées au début.Je pense que compte tenu du temps les gens vont s'ajuster, et de comprendre la division du travail:
auto
en déduit le type sous-jacent, mais vous avez encore envie de penser à la référence-ness et const-ness. Mais c'est en prenant un peu de temps.std::vector
). D'être coûteux pour la copie n'est pas une propriété de classe, mais des objets individuels. Doncmethod_that_returns_reference
peut se référer à un objet d'une classe qui a un constructeur de copie, mais l'objet qui se trouve être assez cher pour copier (et ne peut pas être déplacé à partir de).std::vector
? (Parce qu'il en soit, oui, ou parce que vous n'avez pas le contrôle de la classe, mais ce n'est pas le point) Si c'est cher pour copier, (et ne possède pas de ressources, parce qu'il est copiable), pourquoi ne pas utiliser la VACHE sur l'objet? La localité des données est déjà tué par la taille de l'objet.operator*()&&
surunique_ptr
devrait probablement revenir unT
pas unT&
. Est-ce un défaut dansunique_ptr
, exposés par votre exemple.= delete
la surcharge. Mais plus généralement ce que vous dites est une solution. C'est un sujet que j'ai déjà exploré, si vous êtes intéressé: nirfriedman.com/2016/01/18/....D'autres réponses sont à mentionner les inconvénients comme "vous ne savez pas vraiment ce que le type d'une variable est." Je dirais que c'est en grande partie liée à la bâclée convention de nommage dans le code. Si vos interfaces sont clairement nommées, vous ne devriez pas avoir à soins ce que le type exact est. Bien sûr,
auto result = callSomeFunction(a, b);
ne pas vous en dire beaucoup. Maisauto valid = isValid(xmlFile, schema);
vous dit assez pour utiliservalid
sans avoir à se soucier de ce que son type exact est. Après tout, avec justeif (callSomeFunction(a, b))
, vous ne connaissez pas le type que ce soit. De même avec toute autre sous-expression des objets temporaires. Donc, je ne considère pas cela un réel inconvénient deauto
.Je dirais que son principal inconvénient est que, parfois, exactement le type de retour est pas ce que vous souhaitez travailler avec. En effet, parfois, le rendement réel de type diffère de la "logique" type de retour comme une mise en place/optimisation de détail. L'Expression des modèles en sont un excellent exemple. Disons que nous avons ceci:
Logiquement, nous nous attendons à ce
SomeType
êtreVector
, et nous tenons à le traiter comme tel dans notre code. Toutefois, il est possible que pour l'optimisation, l'algèbre de la bibliothèque, nous utilisons les met en œuvre l'expression de modèles et le type de retour est: est-ceMaintenant, le problème est que
MultExpression<Matrix, Vector>
veut dans toute la probabilité de stocker uneconst Matrix&
etconst Vector&
en interne; il s'attend à ce qu'il va se convertir à uneVector
avant la fin de sa pleine expression. Si nous avons ce code, tout est bien:Cependant, si l'on avait utilisé
auto
ici, nous avons pu mettre en difficulté:auto
a très peu d'inconvénients, si je m'en tiens qu'à force. Et d'autres exemples de procurations, etc. comprennent divers "string " constructeurs" et autres objets trouvés dans les Dsl.auto
le passé, en particulier avec les modes Propres de la bibliothèque. Il est particulièrement difficile parce que le problème n'est souvent pas dans les versions de débogage.auto
peut aussi mordre lors de l'utilisation de la Tatou de la matrice de la bibliothèque, qui fait une utilisation intensive du modèle de méta-programmation à des fins d'optimisation. Heureusement, les développeurs ont ajouté .eval() une fonction qui peut être utilisé pour éviter les problèmes avecauto
auto valid = isValid(...)
est pas plus courte quebool valid = isValid(...)
!isValid
vraiment revenir unbool
, ou est-il une conversion se passe?auto
implique généralement un certain type de contrôle de type (et les éclaboussuresauto
dans l'emmène partout que le type de sécurité à l'écart, comme il le fait partout ailleurs). Ce n'est pas une bonne comparaison.Un des inconvénients est que, parfois, vous ne pouvez pas déclarer
const_iterator
avecauto
. Vous obtiendrez ordinaires (non const) itérateur dans cet exemple de code à partir de cette question:iterator
dans tous les cas, depuis votre carte est non-const
. si vous voulez convertir à unconst_iterator
, spécifiez le type de variable explicitement comme d'habitude, ou extraire une méthode afin que votre carte est const dans le cadre de votrefind
. (Je préfère ce dernier. PÉR.)auto city_it = static_cast<const auto&>(map).find("New York")
? ou, avec le C++17,auto city_if = std::as_const(map).find("New York")
.Il rend votre code un peu plus difficile ou fastidieux à lire.
Imaginez quelque chose comme ça:
Maintenant, de déterminer le type de sortie, vous devez traquer la signature de
doSomethingWithData
fonction.auto it = vec.begin();
est beaucoup plus facile à lire questd::vector<std::wstring>::iterator it = vec.begin();
par exemple.output
est toujours le bon type pour stocker la valeur de retour. Ensuite, si il ya un besoin pour une conversion, j'aimerais spécifier le type exact, ou en fonte. Mais je ajouter quelque chose pour le code que s'il y a quelque chose de louche comme une nécessaire conversion implicite en cours. Mais je suis d'accord que c'est dur d'avoir une indication sur le type exact de la variable avec des noms commeauto
,output
doSomethingWithData
etvariables
.doSomethingWithData
devrait ont pris le soin d'appeler sa méthodecrossProductOf(variables)
, le retour de type aurait été hors de propos.Comme cette développeur, je déteste
auto
. Ou plutôt, je déteste la façon dont les gens de mauvaise utilisationauto
.Je suis de la (forte) de l'opinion que
auto
est pour vous aider à écrire du code générique, non pas pour réduire tapant.C++ est un langage dont le but est de vous permettre d'écrire du code robuste, pas afin de minimiser le temps de développement.
C'est assez évident, d'après de nombreuses fonctionnalités de C++, mais malheureusement quelques-uns des plus récents comme
auto
que de réduire la frappe induire les gens en erreur en pensant qu'ils devraient commencer à être paresseux à taper.En pré-
auto
jours, les genstypedef
s, qui était bien parce quetypedef
a permis au concepteur de la bibliothèque pour vous aider à déterminer quel est le type de retour doit être, de sorte que leur bibliothèque fonctionne comme prévu. Lorsque vous utilisezauto
, vous emporter que le contrôle à partir de la classe de concepteur et au lieu de demander à la compilateur de comprendre ce type devrait être, ce qui supprime l'un des plus puissants C++ outils de la boîte à outils et les risques rupture leur code.En règle générale, si vous utilisez
auto
, elle doit être parce que votre code fonctionne pour tout raisonnable de type, pas parce que vous êtes tout simplement trop paresseux pour écrire le type qu'il devrait travailler avec.Si vous utilisez
auto
comme un outil d'aide à la paresse, puis ce qui se passe est que vous avez éventuellement commencer à introduire bogues subtils dans votre programme, généralement causée par des conversions implicites qui ne se produisent pas parce que vous avez utiliséauto
.Malheureusement, ces bugs sont difficiles à illustrer dans un court exemple ici en raison de leur brièveté rend moins convaincante que les exemples qui viennent d'un utilisateur projet -- cependant, ils se trouvent facilement dans un modèle lourd code qui s'attendent à certains conversions implicites à prendre place.
Si vous voulez un exemple, il y a un ici. Une petite remarque, cependant: avant d'être tenté de sauter et de critiquer le code: gardez à l'esprit que de nombreux bien connu et mature bibliothèques ont été développées autour de ces conversions implicites, et ils sont là parce qu'ils résoudre les problèmes qui peut être difficile si pas impossible à résoudre autrement. Essayez de trouver un meilleure solution avant de les critiquer.
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be
Pas vraiment une bonne raison de l'OMI. Up-to-date de l'IDE, Visual Studio 2015 par exemple, vous permettent de vérifier le type de la variable en survolantauto
. C'est *exactement* le même que letypedef
un.typename std::iterator_traits<It>::value_type
. (2) Le tout le point est que le type inféré besoin de pas être "exactement le même" que le bon type prévu par le précédent concepteur du code; en utilisantauto
, vous prenez loin le concepteur de la possibilité de spécifier le type correct.bitset
est mis en œuvre? Ou considérez-vous peu des conteneurs non-sens tout à fait?!auto
est trop extrême. Pensez à ce qui arrive lorsque, au lieu deauto x = foo(); bar(x);
vous avezbar(foo());
- ici ainsi vous "réduire la frappe" et c'est parfaitement légitime.foo
etbar
ici? (ce que vous voulez montrer?)auto
n'a pas les inconvénients soi, et je plaide pour la main wavily) utilisation partout dans le nouveau code. Il permet à votre code constante de type case, et constamment éviter le silence de la découper. (SiB
dérive deA
et une fonction retournantA
réapparaît subitementB
, puisauto
se comporte comme prévu pour stocker sa valeur de retour)Bien que, avant C++11 du code legacy peut compter sur les conversions implicites induits par l'utilisation de manière explicite-les variables de type. Changer explicitement de type de variable à
auto
peut modifier le code de comportement, de sorte que vous feriez mieux d'être prudent.auto
a des inconvénients en soi (ou au moins beaucoup pensent qu'il n'). Prenons l'exemple donné dans la deuxième question, ce forum avec de Sutter, Alexandrescu et Meyers: Si vous avezauto x = foo(); if (x) { bar(); } else { baz(); }
etfoo()
retournebool
- ce qui se passe sifoo()
les modifications pour revenir un enum (trois options au lieu de deux)? Leauto
code continuera à fonctionner, mais de produire des résultats inattendus.bool
au lieu deauto
changer quoi que ce soit dans le cas d'une non délimité enum? Je me trompe peut-être (ou ne peuvent pas voir ici), mais je pense que la seule différence est que la conversion à l'bool
arrive à la déclaration de la variable à la place de l'évaluation de l'état dans leif
. Si leenum
est étendu, puis conversion enbool
ne doit pas se faire sans un avis précis.Mot-clé
auto
simplement en déduire le type de la valeur de retour. Par conséquent, il n'est pas équivalent à un objet Python, par exempleDepuis
auto
est déduite lors de la compilation, il ne dispose d'aucun inconvénient au moment de l'exécution que ce soit.type()
en python n'. Il en déduit le type, il n'est pas de créer une nouvelle variable de ce type.decltype
.auto
est pour l'attribution de la variable en particulier.Ce que personne n'a mentionné jusqu'ici, mais pour lui-même mérite une réponse, si vous me demandiez.
Depuis (même si chacun doit être conscient que
C != C++
) du code écrit en C peut facilement être conçu de manière à fournir une base pour le code C++ et donc être conçu sans trop d'effort, être en C++ compatible, cela pourrait être une exigence pour la conception.Je sais à propos de certaines règles bien définies constructions de
C
ne sont pas valides pourC++
et vice versa. Mais ce serait tout simplement cassé les exécutables et à l'UB-clause s'applique, ce qui la plupart du temps est remarqué par d'étranges loopings résultant dans des accidents ou que ce soit (ou même peut rester inaperçus, mais qui n'a pas d'importance ici).Mais
auto
est la première fois1 cela change!Imaginez que vous avez utilisé
auto
que le stockage de classe spécificateur avant et transférer le code. Il ne serait même pas nécessairement (selon la manière dont il a été utilisé) "pause"; en fait, il pourrait silencieusement modifier le comportement du programme.C'est quelque chose qu'on doit garder à l'esprit.
1Au moins la première fois que je suis au courant.
int
" en C, vous méritez toutes les mauvaises choses que vous aurez à partir de ce. Et si vous n'êtes pas en s'appuyant sur elle, à l'aide deauto
comme une classe de stockage spécificateur de à côté d'un type vous donnera une belle erreur de compilation en C++ (qui est une Bonne Chose dans ce cas).Comme je l'ai décrit dans cette réponse
auto
peut parfois entraîner funky situations, vous n'avez pas l'intention.Vous devez explicitement dire
auto&
d'avoir un type de référence, tout en faisant justeauto
pouvez créer un type de pointeur. Cela peut entraîner de la confusion en omettant le spécificateur tous ensemble, résultant en une copie de la référence au lieu d'un réel de référence.auto
ne, jamais déduire une référence niconst
type. Pour unauto
de référence, vous feriez mieux d'utiliserauto&&
. (une référence universelle) Si le type n'est pas bon marché pour le copier ou le propriétaire d'une ressource, le type ne doit pas être copiable pour commencer.Une raison que je peux penser, c'est que vous perdez la possibilité de contraindre la classe qui est retournée. Si votre fonction ou de la méthode retourné une longue 64 bits, et vous ne voulait 32 unsigned int, alors vous perdez la possibilité de le contrôler.
Un autre irritant exemple:
génère un avertissement (
comparison between signed and unsigned integer expressions [-Wsign-compare]
), parce quei
est une signature de type int. Pour éviter cela, vous devez écrire par exempleou peut-être mieux:
size
retournesize_t
, vous devriez être en mesure d'avoir unsize_t
littéral comme0z
. Mais vous pouvez déclarer un UDL pour ce faire. (size_t operator""_z(...)
)unsigned
est tout à fait susceptibles de ne pas être presque assez grand pour contenir toutes les valeurs destd::size_t
sur les architectures, donc dans le cas peu probable que quelqu'un a eu un récipient avec un univers gigantesque nombre d'éléments, à l'aide deunsigned
pourrait provoquer une boucle infinie sur le bas de la fourchette d'indices. Tout cela est peu être un problème,std::size_t
doit être utilisé pour obtenir un code propre correctement les signaux de l'intention. Je ne suis pas sûr que mêmeunsigned long long
est strictement garanti suffire, bien que dans la pratique, il doit sans doute être la même.unsigned long long
est assuré d'être au moins 64 bits, mais en théoriesize_t
pourrait être plus grand que cela, je suppose. Bien sûr, si vous avez > 2^64 éléments dans votre conteneur ensuite, vous pouvez avoir plus de problèmes à s'inquiéter... 😉Je pense que
auto
est bien localisé dans un contexte, où le lecteur facilement & évidemment, peut déduire de son type, ou bien documenté avec un commentaire de son type ou un nom qui en déduire le type réel. Ceux qui ne comprennent pas comment cela fonctionne peut prendre dans le mauvais moyens, à l'instar de l'utiliser à la place detemplate
ou similaire. Voici quelques bonnes et mauvaises des cas d'utilisation à mon avis.Bons Usages
Itérateurs
Pointeurs De Fonction
De Mauvais Usages
De Flux De Données
Signature De Fonction
Cas Triviaux
int
, vous devez taper un plus char, si vous allez pourauto
. C'est inacceptableint
aussi facilement vu ici & tapantint
est plus courte. C'est pourquoi il est un cas trivial.