Gibt es einen Nachteil beim Deklarieren von Variablen mit auto in C ++?

143

Es scheint, dass dies autoeine 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 autoder Deklaration von Variablen, anstatt sie explizit zu deklarieren?

DxAlpha
quelle
1
Siehe auch: stackoverflow.com/questions/6434971/… , stackoverflow.com/questions/15254461/… , stackoverflow.com/questions/6900459/… , stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous und vielleicht auch andere. Ich bin mir nicht sicher, ob es sich um exakte Duplikate handelt, aber sie sind definitiv Kandidaten.
Cody Gray
3
Der einzige Nachteil, den ich fand, war, als ich eine Codebasis auf eine (Konsolen-) Plattform portieren musste, deren Compiler C ++ 11-Funktionen nicht unterstützte (und nicht unterstützen wollte)!
Sam
7
Nur der Vollständigkeit halber GotW # 94 "Fast immer automatisch": herbsutter.com/2013/08/12/…
Richard Critten
1
Ich habe cppcast gehört und es wurden Erwähnungen von s / w-Auto- und Listeninitialisierern erwähnt. Ich werde versuchen, diesen Podcast zu finden.
Abhinav Gauniyal
2
Der erste Nachteil, den ich denke, ist die Lesbarkeit des Codes
ggrr

Antworten:

111

Sie haben nur nach Nachteilen gefragt, deshalb hebe ich einige davon hervor. Wenn es gut verwendet wird, autohat 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 autonicht 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

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

Sie wissen nicht unbedingt, um welchen Typ es sich resulthandelt. Es könnte ein sein int. 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 ändern

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

Denn 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 dereferenzieren intund etwas zu ändern, was jetzt ist const). 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, autoals stumpfes Instrument das Kompilieren von Code zu erzwingen, anstatt darüber nachzudenken, was der Code tut, und daran zu arbeiten, ihn richtig zu machen.

Peter
quelle
58
Es ist interessant zu bemerken, dass, wenn solche Beispiele den Nachteil der Verwendung sind auto, die meisten ententypisierten Sprachen von Natur aus einen solchen Nachteil haben!
Leben Asa
11
Wenn CallSomeFunction()je nach Reihenfolge der Argumente ein anderer Typ zurückgegeben wird, ist dies ein Konstruktionsfehler CallSomeFunction()und kein Problem auto. Wenn Sie die Dokumentation einer von Ihnen verwendeten Funktion vor der Verwendung nicht lesen, ist dies ein Defekt des Programmiergeräts und kein Problem auto. - Aber ich verstehe, dass Sie hier den Anwalt des Teufels spielen. Nir Friedman hat nur den viel besseren Fall.
DevSolar
16
@ DevSolar: Warum T CallSomeFunction(T, int, int)sollte ein Designfehler sein? Offensichtlich "gibt es einen anderen Typ zurück, abhängig von der Reihenfolge seiner Argumente."
MSalters
9
"Der Hauptnachteil ist, dass Sie bei der Verwendung autonicht unbedingt den Typ des zu erstellenden Objekts kennen." Können Sie erläutern, warum dies ein Problem mit autound nicht mit Subexpressions-Provisorien ist? Warum ist auto result = foo();schlecht, aber foo().bar()nicht?
Angew ist nicht mehr stolz auf SO
24
Aus Kommentaren geht hervor, dass "Nachteil" als Grund dafür interpretiert wird, dass etwas nicht akzeptabel ist. Ein Nachteil eines Sprachfeatures ist ein Nachteil, den der Entwickler berücksichtigen und begründen muss, ob er akzeptiert oder nicht - dh technische Kompromisse eingehen. Ich mache keine pauschalen Behauptungen darüber, warum die Funktion verwendet werden sollte oder nicht.
Peter
76

Dies ist kein autoprinzipieller Nachteil , aber in der Praxis scheint es für einige ein Problem zu sein. Grundsätzlich können manche Menschen entweder: a) autoals Retter für Typen behandeln und ihr Gehirn abschalten, wenn sie es verwenden, oder b) vergessen, dass dies autoimmer zu Werttypen führt. Dies führt dazu, dass Menschen solche Dinge tun:

auto x = my_obj.method_that_returns_reference();

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:

const auto& stuff = *func_that_returns_unique_ptr();

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, autodass 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: autoleitet den zugrunde liegenden Typ ab, aber Sie möchten immer noch über Referenz und Konstanz nachdenken. Aber es dauert ein bisschen.

Nir Friedman
quelle
Warum können Sie ein teures Objekt zunächst tief kopieren?
Laurent LA RIZZA
3
@LaurentLARIZZA: Einige Klassen haben Kopierkonstruktoren, einfach weil sie manchmal benötigt werden (z. B. Instanzen von std::vector). Das Kopieren ist nicht Eigentum einer Klasse, sondern einzelner Objekte. Dies method_that_returns_referencekö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).
Marc van Leeuwen
@MarcvanLeeuwen: Wenn das Kopieren des Objekts teuer ist und nicht verschoben werden kann, warum sollte es dann in einem Objekt gespeichert werden 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.
Laurent LA RIZZA
2
@LaurentLARIZZA Keine Instanz von etwas, das in einem Vektor gespeichert ist, ist teuer, nur ein regulärer z. B. Vektor <double> ist teuer zu kopieren, es ist eine Heap-Zuweisung + O (N) Arbeit. Umzug ist ein roter Hering. Die erste Zeile, die ich angezeigt habe, wird kopiert und nicht verschoben, es sei denn, die zurückgegebene Referenz ist eine r-Wert-Referenz. COW ist wirklich weder hier noch da. Tatsache ist, dass es immer teuer sein wird, Objekte zu kopieren.
Nir Friedman
4
@ Yakk Das kann man nicht sicher machen, weil es in Scheiben schneiden könnte. Das einzig sichere, was es tun kann, ist = deletediese Ü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/… .
Nir Friedman
51

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. Aber auto valid = isValid(xmlFile, schema);sagt Ihnen genug, um zu verwenden, validohne sich um den genauen Typ kümmern zu müssen. Schließlich if (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 Nachteil auto.

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:

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

Logischerweise würden wir erwarten , SomeTypezu sein Vector, 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:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Nun ist das Problem, dass MultExpression<Matrix, Vector>aller Wahrscheinlichkeit nach ein const Matrix&und const Vector&intern gespeichert wird ; Es erwartet, dass es Vectorvor dem Ende seines vollständigen Ausdrucks in a konvertiert wird . Wenn wir diesen Code haben, ist alles in Ordnung:

extern Matrix a, b, c;
extern Vector v;

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

Wenn wir autohier verwendet hätten, könnten wir jedoch in Schwierigkeiten geraten:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}
Angew ist nicht mehr stolz auf SO
quelle
3
@NirFriedman Du hast Recht, es ist stark, aber ich habe tatsächlich das Gefühl, dass das autonur 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.
Angew ist nicht mehr stolz auf SO
2
Ich wurde von Ausdrucksvorlagen und autofrüher gebissen , speziell von der Eigen-Bibliothek. Dies ist besonders schwierig, da das Problem in Debug-Builds häufig nicht auftritt.
Dan
1
Die Verwendung von autokann 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
mtall
2
"Wenn Ihre Schnittstellen eindeutig benannt sind, sollten Sie sich nicht um den genauen Typ kümmern müssen." Ihr Compiler kann die Richtigkeit Ihres Codes nicht überprüfen, indem er die Namen der Variablen untersucht. Dies ist der gesamte Punkt eines Typsystems. Es blind zu umgehen ist dumm!
Leichtigkeitsrennen im Orbit
1
@Angew: Es ist kein Problem für die Provisorien, da Sie sie normalerweise sofort verwenden, was im autoAllgemeinen keine Art von Typprüfung beinhaltet (und das Einspritzen autoüberall nimmt diese Typensicherheit weg, wie es überall sonst der Fall ist). Es ist kein guter Vergleich.
Leichtigkeitsrennen im Orbit
13

Einer der Nachteile ist , dass man manchmal nicht erklären kann const_iteratormit auto. In diesem Beispiel für Code aus dieser Frage erhalten Sie einen normalen (nicht konstanten) Iterator :

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");
ks1322
quelle
3
Naja, du bekommst iteratorauf jeden Fall eine da deine Karte nicht ist const. Wenn Sie es in a konvertieren möchten, geben Sie const_iteratorentweder den Variablentyp wie gewohnt explizit an oder extrahieren Sie eine Methode, sodass Ihre Map im Kontext von const konstant ist find. (Ich würde letzteres bevorzugen. SRP.)
Laurent LA RIZZA
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").
Dev Null
11

Es macht das Lesen Ihres Codes etwas schwieriger oder mühsamer. Stellen Sie sich so etwas vor:

auto output = doSomethingWithData(variables);

Um nun die Art der Ausgabe herauszufinden, müssten Sie die Signatur der doSomethingWithDataFunktion aufspüren .

Skam
quelle
40
Nicht immer. auto it = vec.begin();ist viel einfacher zu lesen als std::vector<std::wstring>::iterator it = vec.begin();zum Beispiel.
Jonathan Potter
4
Einverstanden. Dies hängt vom Anwendungsfall ab. Ich hätte genauer darauf eingehen können.
Skam
1
@SeeDart Ja, Leute, die Auto so benutzen, machen es falsch.
lciamp
6
"Verfolgen Sie die Funktionssignatur". Wenn dies nicht ein Mauszeiger oder ein Tastendruck ist ("Symbol folgen" / "Zur Deklaration gehen" / wie auch immer es heißt), müssen Sie entweder Ihren Editor mehr konfigurieren oder Wechseln Sie zu einer IDE, die dies ohne Konfiguration tun kann ... Ihr Punkt ist jedoch weiterhin gültig.
Hyde
6
Ich habe festgestellt, dass nicht in einer IDE, sondern in kleinen Gucklöchern von Unterschieden beim Überprüfen von Checkins! Mit Auto sind sie aus diesem Grund schwerer zu lesen.
JDługosz
10

Wie dieser Entwickler hasse ich auto. Oder besser gesagt, ich hasse es, wie Menschen missbrauchen auto.

Ich bin der (starken) Meinung, dass autoes 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 autoreduzieren 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 autoZeiten verwendeten die Leute typedefs, was großartig war, weil typedef der Designer der Bibliothek Ihnen dabei half, herauszufinden, wie der Rückgabetyp aussehen sollte, damit ihre Bibliothek wie erwartet funktioniert. Wenn Sie verwenden auto, 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 Sie autoals 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 sind auto.

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.

user541686
quelle
3
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 beNicht 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 halten auto. Dies ist * genau * das gleiche wie das typedefeine.
Sombrero Huhn
@JameyD: Sie vermissen dort einige wichtige Punkte: (1) Ihr IDE-Argument funktioniert nur, wenn der Typ konkret ist und keine Vorlage enthält. IDEs können Ihnen bei abhängigen Typen, z 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 verwenden autoSie dem Designer die Möglichkeit, den richtigen Typ anzugeben.
user541686
Sie sprechen im Grunde genommen von Proxies, die in einer der Antworten bereits erwähnt werden. Ausdrucksvorlagen und Vektor-Unsinn sind für die meisten Menschen kein alltäglicher Code. In den meisten Situationen möchten Sie keine impliziten Konvertierungen, und Auto hilft dabei. Herb Sutter spricht in einem seiner Blog-Beiträge ausführlich über die Vorteile von Auto. Dabei geht es nicht hauptsächlich um Tastenanschläge, sondern auch nicht nur um generischen Code. Auch der erste Link, den Sie bereitgestellt haben, der Blog-Beitrag ist einfach ein schrecklicher Rat (weshalb er in seinem Kommentarbereich lautstark kritisiert wird).
Nir Friedman
@NirFriedman: "... vector<bool>Unsinn" ... Verzeihung? Wie wird es Ihrer Meinung bitsetnach umgesetzt? Oder halten Sie Bitcontainer für Unsinn?!
user541686
1
@NirFriedman: Nichts über Vektor <bool> ist neu für mich. Was ich Ihnen sagen möchte und dass Sie sich offenkundig weigern zu verstehen, ist, dass sich das Bitset für die Zwecke dieser Frage nicht vom Vektor <bool> unterscheidet - beide verwenden Proxys, da die Proxys als nützlich erachtet wurden und die Tatsache, dass Proxies nützlich sind, ist eine Realität, die Sie akzeptieren müssen, anstatt in Ablehnung zu leben. Können Sie bitte aufhören, daraus eine Debatte darüber zu machen, ob Sie Proxies für nützlich halten? Das ist nicht Gegenstand von Debatten, und auch Ihre Meinung dazu ist nur Ihre Meinung, keine Tatsache.
user541686
6

autohat 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. (Wenn Babgeleitet von Aund eine zurückgebende Funktion Aplötzlich zurückkehrt B, autoverhä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.

Laurent LA RIZZA
quelle
Downvoting ist fair, aber können Sie bitte kommentieren, warum?
Laurent LA RIZZA
Ich habe Sie nicht abgelehnt, habe aber autoNachteile 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 haben auto x = foo(); if (x) { bar(); } else { baz(); }und foo()zurückkehren bool- was passiert, wenn foo()Änderungen vorgenommen werden, um eine Aufzählung zurückzugeben (drei Optionen anstelle von zwei)? Der autoCode funktioniert weiterhin, führt jedoch zu unerwarteten Ergebnissen.
Einpoklum
@einpoklum: Und ändert sich bei Verwendung einer nicht abgedeckten Aufzählung etwas , boolanstatt sie zu autoändern? Ich kann mich irren (kann hier nicht überprüfen), aber ich denke, der einzige Unterschied besteht darin, dass die Konvertierung in boolbei der Variablendeklaration statt bei der Bewertung der Bedingung in der erfolgt if. Wenn der enumGeltungsbereich des Bereichs festgelegt ist, erfolgt die Konvertierung in boolnicht ohne ausdrückliche Ankündigung.
Laurent LA RIZZA
4

Das Schlüsselwort autoleitet den Typ einfach vom Rückgabewert ab. Daher ist es nicht gleichbedeutend mit einem Python-Objekt, z

# 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

Da autoes während der Kompilierung abgeleitet wird, hat es zur Laufzeit keinerlei Nachteile.

Leben Asa
quelle
1
Ja, es macht type()im Grunde das, was in Python macht. Es leitet den Typ ab und erstellt keine neue Variable dieses Typs.
lciamp
2
@lciamp Eigentlich wäre das so decltype. autoist speziell für die Variablenzuweisung.
Cubic
4

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 Cungültig sind C++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 autoals 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ß.

Dhein
quelle
1
Beim Kompilieren wird ohnehin ein Compilerfehler angezeigt.
Sombrero Chicken
@ JamesyD: Was würde das tun? Warum sollten 2 gültige Codesituationen mit unterschiedlicher Bedeutung jemals fehlerhaft neu ausgeführt werden?
Dhein
8
Wenn Sie sich intin 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 Verwendung autoals Speicherklassenspezifizierer neben einem Typ zu einem netten Kompilierungsfehler in C ++ (was in diesem Fall eine gute Sache ist).
Angew ist nicht mehr stolz auf SO
1
@Angew gut, das ist der Fall, auf den ich mich beziehe, ja. Ich mache das nicht Aber es ist etwas, das zumindest im Auge behalten werden sollte.
Dhein
3

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.

kblansit
quelle
1
Es gibt static_cast, und IIRC, zum Beispiel Meyers 'Effective Modern C ++, empfiehlt sogar, es zu verwenden, um den Typ für automatisch typisierte Variablen anzugeben.
Hyde
2

Wie ich in dieser Antwort beschrieben habe,auto kann es manchmal zu funkigen Situationen kommen, die Sie nicht beabsichtigt haben. Sie müssen explizit sagen auto&, um einen Referenztyp zu haben, während Sie nur einen Zeigertyp autoerstellen 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.

Sombrero Huhn
quelle
2
Das ist nicht funky. Das ist es, was autoniemals auf eine Referenz oder einen constTyp schließen lässt. Als autoReferenz sollten Sie besser verwenden auto&&. (eine universelle Referenz) Wenn der Typ nicht billig zu kopieren ist oder eine Ressource besitzt, sollte der Typ zunächst nicht kopierbar sein.
Laurent LA RIZZA
1

Ein weiteres irritierendes Beispiel:

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

generiert eine Warnung ( comparison between signed and unsigned integer expressions [-Wsign-compare]), da ies sich um ein signiertes int handelt. Um dies zu vermeiden, müssen Sie z

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

oder vielleicht besser:

for (auto i = 0ULL; i < s.size(); ++i)
Paul R.
quelle
1
Ja, ich finde das auch irritierend. Aber das Loch in der Sprache ist woanders. Damit dieser Code wirklich tragbar sein, vorausgesetzt , sizekehrt size_t, würden Sie einen haben , in der Lage sein size_twie wörtliche 0z. Sie können jedoch eine UDL deklarieren, um dies zu tun. ( size_t operator""_z(...))
Laurent LA RIZZA
1
Rein theoretische Einwände: Es unsignedist sehr wahrscheinlich, dass sie nicht annähernd groß genug sind, um alle Werte von std::size_tMainstream-Architekturen aufzunehmen. In dem unwahrscheinlichen Fall, dass jemand einen Container mit einer absurd gigantischen Anzahl von Elementen hatte, unsignedkönnte die Verwendung eine Endlosschleife über den unteren Bereich verursachen von Indizes. Dies ist zwar wahrscheinlich kein Problem, std::size_tsollte jedoch verwendet werden, um sauberen Code zu erhalten, der die Absicht ordnungsgemäß signalisiert. Ich bin mir nicht sicher, ob dies unsigned long longunbedingt ausreicht, obwohl es in der Praxis vermutlich dasselbe sein muss.
underscore_d
@underscore_d: Ja, fairer Punkt - unsigned long longgarantiert mindestens 64 Bit, aber theoretisch size_tkö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 ... ;-)
Paul R
1

Ich denke, es autoist 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 von templateoder ähnlich verwenden. Hier sind meiner Meinung nach einige gute und schlechte Anwendungsfälle.

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Gute Verwendung

Iteratoren

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Funktionszeiger

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Schlechte Verwendung

Datenfluss

auto input = "";

..

auto output = test(input);

Funktionssignatur

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

Trivialfälle

for(auto i = 0; i < 100; i++)
{
    ..
}
Khaled.K
quelle
Wenn Sie eine möchten int, müssen Sie ein weiteres Zeichen eingeben, wenn Sie möchten auto. Das ist inakzeptabel
Rerito
@Rerito ja, es intist hier genauso leicht zu sehen und das Tippen intist kürzer. Deshalb ist es ein trivialer Fall.
Khaled.K
0

Ich bin überrascht, dass dies niemand erwähnt hat, aber nehmen wir an, Sie berechnen die Fakultät von etwas:

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

Dieser Code gibt Folgendes aus:

Factorial of 40 = 0
Size of factorial: 4

Das war definitiv nicht das erwartete Ergebnis. Dies geschah, weil autoder Typ der Variablen Fakultät abgeleitet wurde, intweil sie zugewiesen wurde 1.

xilpex
quelle