Es scheint, dass dies auto
eine ziemlich wichtige Funktion war, die in C ++ 11 hinzugefügt wurde und die vielen neueren Sprachen zu folgen scheint. Wie bei einer Sprache wie Python habe ich keine explizite Variablendeklaration gesehen (ich bin nicht sicher, ob es mit Python-Standards möglich ist).
Gibt es einen Nachteil bei auto
der Deklaration von Variablen, anstatt sie explizit zu deklarieren?
c++
c++11
type-inference
auto
DxAlpha
quelle
quelle
Antworten:
Sie haben nur nach Nachteilen gefragt, deshalb hebe ich einige davon hervor. Wenn es gut verwendet wird,
auto
hat es auch mehrere Vorteile. Die Nachteile resultieren aus der Leichtigkeit des Missbrauchs und dem erhöhten Potenzial für Code, sich unbeabsichtigt zu verhalten.Der Hauptnachteil besteht darin, dass Sie bei der Verwendung
auto
nicht unbedingt den Typ des zu erstellenden Objekts kennen. Es gibt auch Fälle, in denen der Programmierer erwartet, dass der Compiler einen Typ ableitet, der Compiler jedoch unerbittlich einen anderen.Gegeben eine Erklärung wie
Sie wissen nicht unbedingt, um welchen Typ es sich
result
handelt. Es könnte ein seinint
. Es könnte ein Zeiger sein. Es könnte etwas anderes sein. Alle diese unterstützen unterschiedliche Operationen. Sie können den Code auch durch eine geringfügige Änderung wie dramatisch ändernDenn je nachdem, welche Überladungen für
CallSomeFunction()
den Ergebnistyp vorhanden sind, kann dies völlig anders sein - und nachfolgender Code kann sich daher völlig anders verhalten als beabsichtigt. Sie könnten plötzlich Fehlermeldungen in späterem Code auslösen (z. B. versuchen Sie anschließend, eine dereferenzierenint
und etwas zu ändern, was jetzt istconst
). Die unheimlichere Änderung ist, wo Ihre Änderung am Compiler vorbeigeht, aber nachfolgender Code sich auf unterschiedliche und unbekannte - möglicherweise fehlerhafte - Weise verhält.Wenn der Typ einiger Variablen nicht explizit bekannt ist, ist es daher schwieriger, eine Behauptung, dass der Code wie beabsichtigt funktioniert, rigoros zu rechtfertigen. Dies bedeutet mehr Aufwand, um Behauptungen von "Zweckmäßigkeit" in Bereichen mit hoher Kritikalität (z. B. sicherheitskritisch oder geschäftskritisch) zu rechtfertigen.
Der andere, häufigere Nachteil ist die Versuchung eines Programmierers,
auto
als stumpfes Instrument das Kompilieren von Code zu erzwingen, anstatt darüber nachzudenken, was der Code tut, und daran zu arbeiten, ihn richtig zu machen.quelle
auto
, die meisten ententypisierten Sprachen von Natur aus einen solchen Nachteil haben!CallSomeFunction()
je nach Reihenfolge der Argumente ein anderer Typ zurückgegeben wird, ist dies ein KonstruktionsfehlerCallSomeFunction()
und kein Problemauto
. Wenn Sie die Dokumentation einer von Ihnen verwendeten Funktion vor der Verwendung nicht lesen, ist dies ein Defekt des Programmiergeräts und kein Problemauto
. - Aber ich verstehe, dass Sie hier den Anwalt des Teufels spielen. Nir Friedman hat nur den viel besseren Fall.T CallSomeFunction(T, int, int)
sollte ein Designfehler sein? Offensichtlich "gibt es einen anderen Typ zurück, abhängig von der Reihenfolge seiner Argumente."auto
nicht unbedingt den Typ des zu erstellenden Objekts kennen." Können Sie erläutern, warum dies ein Problem mitauto
und nicht mit Subexpressions-Provisorien ist? Warum istauto result = foo();
schlecht, aberfoo().bar()
nicht?Dies ist kein
auto
prinzipieller Nachteil , aber in der Praxis scheint es für einige ein Problem zu sein. Grundsätzlich können manche Menschen entweder: a)auto
als Retter für Typen behandeln und ihr Gehirn abschalten, wenn sie es verwenden, oder b) vergessen, dass diesauto
immer zu Werttypen führt. Dies führt dazu, dass Menschen solche Dinge tun:Hoppla, wir haben gerade ein Objekt tief kopiert. Es ist oft entweder ein Fehler oder ein Leistungsfehler. Dann können Sie auch in die andere Richtung schwingen:
Jetzt erhalten Sie eine baumelnde Referenz. Diese Probleme werden überhaupt nicht verursacht
auto
, daher halte ich sie nicht für legitime Argumente dagegen. Aber es scheint,auto
dass diese Probleme (aus meiner persönlichen Erfahrung) aus den Gründen, die ich am Anfang aufgeführt habe, häufiger auftreten.Ich denke, mit der Zeit werden sich die Menschen anpassen und die Arbeitsteilung verstehen:
auto
leitet den zugrunde liegenden Typ ab, aber Sie möchten immer noch über Referenz und Konstanz nachdenken. Aber es dauert ein bisschen.quelle
std::vector
). Das Kopieren ist nicht Eigentum einer Klasse, sondern einzelner Objekte. Diesmethod_that_returns_reference
könnte sich also auf ein Objekt einer Klasse beziehen, das über einen Kopierkonstruktor verfügt, dessen Kopieren jedoch recht teuer ist (und von dem nicht verschoben werden kann).std::vector
? (Weil es vielleicht ja ist oder weil Sie die Klasse nicht kontrollieren, aber das ist nicht der Punkt.) Wenn das Kopieren teuer ist (und keine Ressource besitzt, weil es kopierbar ist), warum nicht COW für das Objekt verwenden? Die Datenlokalität wird bereits durch die Größe des Objekts zerstört.= delete
diese Überlastung. Im Allgemeinen ist das, was Sie sagen, eine Lösung. Dies ist ein Thema, das ich untersucht habe, wenn Sie interessiert sind: nirfriedman.com/2016/01/18/… .Andere Antworten erwähnen Nachteile wie "Sie wissen nicht wirklich, was der Typ einer Variablen ist." Ich würde sagen, dass dies größtenteils mit der schlampigen Namenskonvention im Code zusammenhängt. Wenn Ihre Schnittstellen eindeutig benannt sind, sollten Sie sich nicht um den genauen Typ kümmern müssen . Klar,
auto result = callSomeFunction(a, b);
sagt dir nicht viel. Aberauto valid = isValid(xmlFile, schema);
sagt Ihnen genug, um zu verwenden,valid
ohne sich um den genauen Typ kümmern zu müssen. Schließlichif (callSomeFunction(a, b))
würden Sie mit just auch den Typ nicht kennen. Das Gleiche gilt für alle anderen temporären Objekte mit Unterausdrücken. Ich halte dies also nicht für einen wirklichen Nachteilauto
.Ich würde sagen, der Hauptnachteil ist, dass manchmal der genaue Rückgabetyp nicht das ist , mit dem Sie arbeiten möchten. Tatsächlich unterscheidet sich der tatsächliche Rückgabetyp manchmal vom "logischen" Rückgabetyp als Implementierungs- / Optimierungsdetail. Ausdrucksvorlagen sind ein Paradebeispiel. Nehmen wir an, wir haben Folgendes:
Logischerweise würden wir erwarten ,
SomeType
zu seinVector
, und wir wollen es auf jeden Fall als solche behandeln in unserem Code. Es ist jedoch möglich, dass die von uns verwendete Algebra-Bibliothek zu Optimierungszwecken Ausdrucksvorlagen implementiert. Der tatsächliche Rückgabetyp lautet wie folgt:Nun ist das Problem, dass
MultExpression<Matrix, Vector>
aller Wahrscheinlichkeit nach einconst Matrix&
undconst Vector&
intern gespeichert wird ; Es erwartet, dass esVector
vor dem Ende seines vollständigen Ausdrucks in a konvertiert wird . Wenn wir diesen Code haben, ist alles in Ordnung:Wenn wir
auto
hier verwendet hätten, könnten wir jedoch in Schwierigkeiten geraten:quelle
auto
nur sehr wenige Nachteile hat, also stehe ich zu dieser Stärke. Andere Beispiele für Proxys usw. umfassen verschiedene "String Builder" und ähnliche Objekte, die in DSLs gefunden werden.auto
früher gebissen , speziell von der Eigen-Bibliothek. Dies ist besonders schwierig, da das Problem in Debug-Builds häufig nicht auftritt.auto
kann auch beißen, wenn die Armadillo- Matrixbibliothek verwendet wird, die die Vorlagen-Metaprogrammierung für Optimierungszwecke stark nutzt. Glücklicherweise haben die Entwickler die Funktion .eval () hinzugefügt , mit der Probleme mitauto
auto
Allgemeinen keine Art von Typprüfung beinhaltet (und das Einspritzenauto
überall nimmt diese Typensicherheit weg, wie es überall sonst der Fall ist). Es ist kein guter Vergleich.Einer der Nachteile ist , dass man manchmal nicht erklären kann
const_iterator
mitauto
. In diesem Beispiel für Code aus dieser Frage erhalten Sie einen normalen (nicht konstanten) Iterator :quelle
iterator
auf jeden Fall eine da deine Karte nicht istconst
. Wenn Sie es in a konvertieren möchten, geben Sieconst_iterator
entweder den Variablentyp wie gewohnt explizit an oder extrahieren Sie eine Methode, sodass Ihre Map im Kontext von const konstant istfind
. (Ich würde letzteres bevorzugen. SRP.)auto city_it = static_cast<const auto&>(map).find("New York")
? oder mit C ++ 17 ,auto city_if = std::as_const(map).find("New York")
.Es macht das Lesen Ihres Codes etwas schwieriger oder mühsamer. Stellen Sie sich so etwas vor:
Um nun die Art der Ausgabe herauszufinden, müssten Sie die Signatur der
doSomethingWithData
Funktion aufspüren .quelle
auto it = vec.begin();
ist viel einfacher zu lesen alsstd::vector<std::wstring>::iterator it = vec.begin();
zum Beispiel.Wie dieser Entwickler hasse ich
auto
. Oder besser gesagt, ich hasse es, wie Menschen missbrauchenauto
.Ich bin der (starken) Meinung, dass
auto
es Ihnen helfen soll, generischen Code zu schreiben, und nicht, die Eingabe zu reduzieren .C ++ ist eine Sprache, deren Ziel es ist, robusten Code zu schreiben und die Entwicklungszeit nicht zu minimieren.
Dies ist aus vielen Funktionen von C ++ ziemlich offensichtlich, aber leider
auto
reduzieren einige der neueren Funktionen, die das Tippen reduzieren, die Leute in die Irre, zu denken, sie sollten anfangen, faul mit dem Tippen zu sein.In früheren
auto
Zeiten verwendeten die Leutetypedef
s, was großartig war, weiltypedef
der Designer der Bibliothek Ihnen dabei half, herauszufinden, wie der Rückgabetyp aussehen sollte, damit ihre Bibliothek wie erwartet funktioniert. Wenn Sie verwendenauto
, entfernen Sie dieses Steuerelement vom Designer der Klasse und bitten stattdessen den Compiler , herauszufinden, welcher Typ sein soll. Dadurch wird eines der leistungsstärksten C ++ - Tools aus der Toolbox entfernt und es besteht die Gefahr, dass der Code beschädigt wird.Wenn Sie verwenden
auto
, sollte dies im Allgemeinen daran liegen, dass Ihr Code für jeden vernünftigen Typ funktioniert , und nicht daran, dass Sie einfach zu faul sind, den Typ aufzuschreiben, mit dem er funktionieren soll. Wenn Sieauto
als Tool zur Unterstützung der Faulheit verwenden, führen Sie schließlich subtile Fehler in Ihr Programm ein, die normalerweise durch implizite Konvertierungen verursacht werden, die aufgrund Ihrer Verwendung nicht aufgetreten sindauto
.Leider sind diese Fehler in einem kurzen Beispiel hier nur schwer zu veranschaulichen , da sie aufgrund ihrer Kürze weniger überzeugend sind als die tatsächlichen Beispiele in einem Benutzerprojekt. Sie treten jedoch leicht in vorlagenlastigem Code auf, für den bestimmte implizite Konvertierungen erforderlich sind Ort.
Wenn Sie ein Beispiel möchten, gibt es hier eines . Ein kleiner Hinweis: Bevor Sie versucht sind, den Code zu überspringen und zu kritisieren, denken Sie daran, dass viele bekannte und ausgereifte Bibliotheken um solche impliziten Konvertierungen herum entwickelt wurden und sie vorhanden sind, weil sie Probleme lösen , die schwierig, wenn nicht unmöglich sein können anders zu lösen. Versuchen Sie, eine bessere Lösung zu finden, bevor Sie sie kritisieren.
quelle
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
Nicht wirklich ein guter Grund IMO. Mit aktuellen IDEs, beispielsweise Visual Studio 2015, können Sie den Typ der Variablen überprüfen, indem Sie den Mauszeiger darüber haltenauto
. Dies ist * genau * das gleiche wie dastypedef
eine.typename std::iterator_traits<It>::value_type
. (2) Der ganze Punkt war, dass der abgeleitete Typ nicht "genau derselbe" sein muss wie der richtige Typ, der vom vorherigen Designer des Codes beabsichtigt wurde; Mit verwendenauto
Sie dem Designer die Möglichkeit, den richtigen Typ anzugeben.vector<bool>
Unsinn" ... Verzeihung? Wie wird es Ihrer Meinungbitset
nach umgesetzt? Oder halten Sie Bitcontainer für Unsinn?!auto
hat keine Nachteile für sich , und ich befürworte bis (Hand-wavily) verwenden Sie es überall in den neuen Code. Es ermöglicht Ihrem Code eine konsistente Typprüfung und ein konsistentes Silent Slicing. (WennB
abgeleitet vonA
und eine zurückgebende FunktionA
plötzlich zurückkehrtB
,auto
verhält sie sich wie erwartet, um ihren Rückgabewert zu speichern.)Legacy-Code vor C ++ 11 kann jedoch auf impliziten Konvertierungen beruhen, die durch die Verwendung explizit typisierter Variablen induziert werden. Wenn Sie eine explizit typisierte Variable
auto
ändern , um das Codeverhalten zu ändern , sollten Sie vorsichtig sein.quelle
auto
Nachteile an sich (oder zumindest - viele denken, dass dies der Fall ist). Betrachten Sie das Beispiel in der zweiten Frage in dieser Podiumsdiskussion mit Sutter, Alexandrescu und Meyers: Wenn Sie habenauto x = foo(); if (x) { bar(); } else { baz(); }
undfoo()
zurückkehrenbool
- was passiert, wennfoo()
Änderungen vorgenommen werden, um eine Aufzählung zurückzugeben (drei Optionen anstelle von zwei)? Derauto
Code funktioniert weiterhin, führt jedoch zu unerwarteten Ergebnissen.bool
anstatt sie zuauto
ändern? Ich kann mich irren (kann hier nicht überprüfen), aber ich denke, der einzige Unterschied besteht darin, dass die Konvertierung inbool
bei der Variablendeklaration statt bei der Bewertung der Bedingung in der erfolgtif
. Wenn derenum
Geltungsbereich des Bereichs festgelegt ist, erfolgt die Konvertierung inbool
nicht ohne ausdrückliche Ankündigung.Das Schlüsselwort
auto
leitet den Typ einfach vom Rückgabewert ab. Daher ist es nicht gleichbedeutend mit einem Python-Objekt, zDa
auto
es während der Kompilierung abgeleitet wird, hat es zur Laufzeit keinerlei Nachteile.quelle
type()
im Grunde das, was in Python macht. Es leitet den Typ ab und erstellt keine neue Variable dieses Typs.decltype
.auto
ist speziell für die Variablenzuweisung.Was hier bisher niemand erwähnt hat, aber für sich ist eine Antwort wert, wenn Sie mich fragen.
Da (auch wenn jeder wissen sollte, dass
C != C++
) in C geschriebener Code leicht als Basis für C ++ - Code entworfen werden kann und daher ohne großen Aufwand entworfen werden kann, um C ++ - kompatibel zu sein, könnte dies eine Voraussetzung für das Design sein.Ich kenne einige Regeln, für die einige gut definierte Konstrukte
C
ungültig sindC++
und umgekehrt. Dies würde jedoch einfach zu fehlerhaften ausführbaren Dateien führen, und es gilt die bekannte UB-Klausel, die meistens durch seltsame Schleifen bemerkt wird, die zu Abstürzen oder was auch immer führen (oder sogar unentdeckt bleiben können, aber das spielt hier keine Rolle).Aber
auto
ist das erste Mal 1 diese Änderungen!Stellen Sie sich vor, Sie haben zuvor
auto
als Speicherklassenspezifizierer verwendet und übertragen den Code. Es würde nicht einmal notwendigerweise (abhängig von der Art und Weise, wie es verwendet wurde) "brechen"; es könnte tatsächlich stillschweigend das Verhalten des Programms ändern.Daran sollte man denken.
1 Zumindest das erste Mal, dass ich es weiß.
quelle
int
in C auf "kein Typ impliziert " verlassen, verdienen Sie all die schlechten Dinge, die Sie daraus erhalten. Und wenn Sie sich nicht darauf verlassen, führt die Verwendungauto
als Speicherklassenspezifizierer neben einem Typ zu einem netten Kompilierungsfehler in C ++ (was in diesem Fall eine gute Sache ist).Ein Grund, an den ich denken kann, ist, dass Sie die Gelegenheit verlieren, die zurückgegebene Klasse zu zwingen. Wenn Ihre Funktion oder Methode ein langes 64-Bit zurückgegeben hat und Sie nur ein 32-Int ohne Vorzeichen wollten, verlieren Sie die Möglichkeit, dies zu steuern.
quelle
Wie ich in dieser Antwort beschrieben habe,
auto
kann es manchmal zu funkigen Situationen kommen, die Sie nicht beabsichtigt haben. Sie müssen explizit sagenauto&
, um einen Referenztyp zu haben, während Sie nur einen Zeigertypauto
erstellen können. Dies kann zu Verwirrung führen, indem der Spezifizierer insgesamt weggelassen wird, was zu einer Kopie der Referenz anstelle einer tatsächlichen Referenz führt.quelle
auto
niemals auf eine Referenz oder einenconst
Typ schließen lässt. Alsauto
Referenz sollten Sie besser verwendenauto&&
. (eine universelle Referenz) Wenn der Typ nicht billig zu kopieren ist oder eine Ressource besitzt, sollte der Typ zunächst nicht kopierbar sein.Ein weiteres irritierendes Beispiel:
generiert eine Warnung (
comparison between signed and unsigned integer expressions [-Wsign-compare]
), dai
es sich um ein signiertes int handelt. Um dies zu vermeiden, müssen Sie zoder vielleicht besser:
quelle
size
kehrtsize_t
, würden Sie einen haben , in der Lage seinsize_t
wie wörtliche0z
. Sie können jedoch eine UDL deklarieren, um dies zu tun. (size_t operator""_z(...)
)unsigned
ist sehr wahrscheinlich, dass sie nicht annähernd groß genug sind, um alle Werte vonstd::size_t
Mainstream-Architekturen aufzunehmen. In dem unwahrscheinlichen Fall, dass jemand einen Container mit einer absurd gigantischen Anzahl von Elementen hatte,unsigned
könnte die Verwendung eine Endlosschleife über den unteren Bereich verursachen von Indizes. Dies ist zwar wahrscheinlich kein Problem,std::size_t
sollte jedoch verwendet werden, um sauberen Code zu erhalten, der die Absicht ordnungsgemäß signalisiert. Ich bin mir nicht sicher, ob diesunsigned long long
unbedingt ausreicht, obwohl es in der Praxis vermutlich dasselbe sein muss.unsigned long long
garantiert mindestens 64 Bit, aber theoretischsize_t
könnte es größer sein, nehme ich an. Natürlich, wenn Sie> 2 ^ 64 Elemente in Ihrem Container haben, dann können Sie größere Probleme haben, über die Sie sich Sorgen machen müssen ... ;-)Ich denke, es
auto
ist gut, wenn es in einem lokalisierten Kontext verwendet wird, in dem der Leser seinen Typ leicht und offensichtlich ableiten kann, oder gut dokumentiert mit einem Kommentar seines Typs oder einem Namen, der auf den tatsächlichen Typ schließen lässt. Diejenigen, die nicht verstehen, wie es funktioniert, nehmen es möglicherweise falsch, wie wenn sie es anstelle vontemplate
oder ähnlich verwenden. Hier sind meiner Meinung nach einige gute und schlechte Anwendungsfälle.Gute Verwendung
Iteratoren
Funktionszeiger
Schlechte Verwendung
Datenfluss
Funktionssignatur
Trivialfälle
quelle
int
, müssen Sie ein weiteres Zeichen eingeben, wenn Sie möchtenauto
. Das ist inakzeptabelint
ist hier genauso leicht zu sehen und das Tippenint
ist kürzer. Deshalb ist es ein trivialer Fall.Ich bin überrascht, dass dies niemand erwähnt hat, aber nehmen wir an, Sie berechnen die Fakultät von etwas:
Dieser Code gibt Folgendes aus:
Das war definitiv nicht das erwartete Ergebnis. Dies geschah, weil
auto
der Typ der Variablen Fakultät abgeleitet wurde,int
weil sie zugewiesen wurde1
.quelle