Erschwert Auto das Verständnis von C ++ - Code?


122

Ich habe eine Konferenz von Herb Sutter gesehen, in der er jeden C ++ - Programmierer ermutigt, sie zu verwenden auto.

Ich musste vor einiger Zeit C # -Code lesen, wo er häufig varverwendet wurde, und der Code war sehr schwer zu verstehen. Bei jeder varVerwendung musste ich den Rückgabetyp auf der rechten Seite überprüfen. Manchmal mehr als einmal, weil ich den Typ der Variablen nach einer Weile vergessen habe!

Ich weiß, dass der Compiler den Typ kennt und ich ihn nicht schreiben muss, aber es ist allgemein anerkannt, dass wir Code für Programmierer schreiben sollten, nicht für Compiler.

Ich weiß auch, dass das einfacher zu schreiben ist:

auto x = GetX();

Als:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

Dies wird jedoch nur einmal geschrieben und der GetX()Rückgabetyp wird mehrmals überprüft, um zu verstehen, welchen Typ er xhat.

Das hat mich gefragt: autoIst C ++ - Code schwieriger zu verstehen?


29
Müssen Sie den Rückgabetyp wirklich jedes Mal überprüfen ? Warum ist der Typ nicht aus dem Code ersichtlich? autokann die Lesbarkeit von Objekten oft erschweren, wenn sie bereits schwer lesbar sind, z. B. zu lange Funktionen, schlecht benannte Variablen usw. Bei kurzen Funktionen mit anständig benannten Variablen sollte die Kenntnis der Typen eine der Kategorien # 1 easy oder # 2 irrelevant sein.
R. Martinho Fernandes

25
Die "Kunst" der Verwendung autoähnelt der Bestimmung des Verwendungszeitpunkts typedef. Es liegt an Ihnen zu bestimmen, wann es behindert und wann es hilft.
Ahenderson

18
Ich dachte, ich hätte das gleiche Problem, aber dann wurde mir klar, dass ich den Code nur verstehen kann, ohne die Typen zu kennen. Beispiel: "auto idx = get_index ();" IDX ist also etwas, das einen Index enthält. Was der genaue Typ ist, ist für die meisten Fälle völlig irrelevant.
PlasmaHH

31
Also schreibe nicht auto x = GetX();, wähle einen besseren Namen als der x, der dir tatsächlich sagt, was er in diesem speziellen Kontext tut ... das ist sowieso oft nützlicher als sein Typ.
Jonathan Wakely

11
Wenn die Verwendung von mehr Typinferenz es einem Programmierer erschwert, den Code zu lesen, muss entweder der Code oder der Programmierer ernsthaft verbessert werden.
CA McCann

Antworten:


100

Kurze Antwort: Meiner Meinung nach autosollten Sie autostandardmäßig verwenden, es sei denn, Sie möchten ausdrücklich eine Konvertierung. (Etwas genauer: "... es sei denn, Sie möchten explizit einen Typ festlegen, was fast immer daran liegt, dass Sie eine Konvertierung wünschen.")

Längere Antwort und Begründung:

Schreiben Sie einen expliziten Typ (anstatt auto) nur dann, wenn Sie sich wirklich explizit auf einen Typ festlegen möchten, was fast immer bedeutet, dass Sie explizit eine Konvertierung in diesen Typ durchführen möchten. Ich erinnere mich an zwei Hauptfälle:

  • (Common) Die initializer_listÜberraschung, die auto x = { 1 };ableitet initializer_list. Wenn Sie nicht möchten initializer_list, geben Sie den Typ an, dh fordern Sie explizit eine Konvertierung an.
  • (Selten) Der Ausdrucksvorlagen-Fall, bei dem beispielsweise auto x = matrix1 * matrix 2 + matrix3;ein Hilfs- oder Proxy-Typ erfasst wird, der für den Programmierer nicht sichtbar sein soll. In vielen Fällen ist es in Ordnung und harmlos, diesen Typ zu erfassen, aber manchmal, wenn Sie wirklich wollen, dass er zusammenbricht und die Berechnung durchführt, sagen Sie den Typ - dh fragen Sie erneut explizit nach einer Konvertierung.

Andernfalls autostandardmäßig routinemäßig verwenden , da die Verwendung von autoFallstricken vermeidet und den Code korrekter, wartbarer, robuster und effizienter macht. Etwa in der Reihenfolge von am wenigsten wichtig, im Sinne von "Schreiben Sie zuerst für Klarheit und Korrektheit":

  • Richtigkeit: Mit autoGarantien erhalten Sie den richtigen Typ. Wie das Sprichwort sagt, wenn Sie sich wiederholen (sagen Sie den Typ überflüssig), können und werden Sie lügen (verstehen Sie es falsch). Hier ein übliches Beispiel: void f( const vector<int>& v ) { for( /*…*- Wenn Sie an dieser Stelle den Typ des Iterators explizit schreiben, möchten Sie sich daran erinnern, dass Sie geschrieben haben const_iterator(oder?), Während Sie es autoeinfach richtig machen.
  • Wartbarkeit und Robustheit: Durch Verwendung von autowird der Code angesichts von Änderungen robuster, da er bei Änderungen des Ausdruckstyps autoweiterhin in den richtigen Typ aufgelöst wird. Wenn Sie stattdessen einen expliziten Typ festlegen, werden beim Ändern des Ausdruckstyps stille Konvertierungen eingefügt, wenn der neue Typ in den alten Typ konvertiert wird, oder unnötige Build-Unterbrechungen, wenn der neue Typ weiterhin wie der alte Typ funktioniert, aber nicht in den alten konvertiert wird Typ (zum Beispiel, wenn Sie eine Änderung mapan eine unordered_map, die immer in Ordnung ist , wenn Sie nicht auf Bestellung angewiesen sind, verwenden autofür Ihre Iteratoren werden Sie nahtlos wechseln von map<>::iteratorzu unordered_map<>::iterator, jedoch unter Verwendung vonmap<>::iterator Überall explizit bedeutet, dass Sie Ihre wertvolle Zeit mit einem mechanischen Code-Fixing verschwenden, es sei denn, ein Praktikant kommt vorbei und Sie können die langweilige Arbeit auf sich nehmen.
  • Leistung: Da autogarantiert wird, dass keine implizite Konvertierung erfolgt, wird standardmäßig eine bessere Leistung garantiert. Wenn Sie stattdessen den Typ angeben und eine Konvertierung erforderlich ist, erhalten Sie häufig im Hintergrund eine Konvertierung, unabhängig davon, ob Sie dies erwartet haben oder nicht.
  • Benutzerfreundlichkeit: Die Verwendung von autoist Ihre einzige gute Option für schwer zu buchstabierende und nicht auszusprechende Typen wie Lambdas und Template-Helfer, ohne auf sich wiederholende decltypeAusdrücke oder weniger effiziente Indirektionen wie zuzugreifen std::function.
  • Bequemlichkeit: Und, ja, es autowird weniger getippt. Ich erwähne dies der Vollständigkeit halber, weil es ein häufiger Grund ist, es zu mögen, aber es ist nicht der größte Grund, es zu benutzen.

Daher: Ziehe es vor, autostandardmäßig zu sagen . Es bietet so viel Einfachheit, Leistung und Klarheit, dass Sie sich selbst (und die zukünftigen Betreuer Ihres Codes) nur verletzen, wenn Sie dies nicht tun. Festschreiben Sie einen expliziten Typ nur dann, wenn Sie ihn wirklich meinen, was fast immer bedeutet, dass Sie eine explizite Konvertierung wünschen.

Ja, dazu gibt es (jetzt) ​​ein GotW .


14
Ich finde auto auch dann nützlich, wenn ich eine Konvertierung möchte. Es erlaubt mir , explizit für eine Umwandlung zu stellen , ohne die Art zu wiederholen: auto x = static_cast<X>(y). Das static_castmacht deutlich, dass die Konvertierung absichtlich ist und vermeidet Compiler-Warnungen über die Konvertierung. Normalerweise ist es nicht so gut, Compiler-Warnungen zu vermeiden, aber ich kann keine Warnung über eine Konvertierung erhalten, die ich beim Schreiben sorgfältig in Betracht gezogen habe static_cast. Ich würde das zwar nicht tun, wenn es jetzt keine Warnungen gibt, aber ich möchte in Zukunft Warnungen erhalten, wenn sich die Typen auf eine potenziell gefährliche Weise ändern.
Bjarke Hammersholt Roune

6
Eine Sache, die ich finde, autoist, dass wir uns bemühen sollten, gegen Schnittstellen zu programmieren (nicht im Sinne von OOP), nicht gegen bestimmte Implementierungen. Genau so ist es mit Vorlagen. Beschweren Sie sich über "schwer lesbaren Code", weil Sie einen Vorlagentyp-Parameter haben T, der überall verwendet wird? Nein, das glaube ich nicht. Auch in Templates wird gegen eine Schnittstelle codiert, was viele Leute Entenschreiben zur Kompilierungszeit nennen.
Xeo

6
"Mit Auto-Garantien erhalten Sie den richtigen Typ." Überhaupt nicht wahr. Es wird nur garantiert, dass Sie den Typ erhalten, der von einem anderen Teil Ihres Codes vorgeschrieben wird. Ob das stimmt oder nicht, ist völlig unklar, wenn Sie es hinter sich verstecken auto.
Leichtigkeitsrennen im Orbit

Ich bin wirklich überrascht, dass sich niemand um IDEs kümmert ... Selbst moderne IDEs unterstützen das Springen zur Klassen- / Strukturdefinition im Fall von autoVariablen nicht korrekt , aber fast alle tun es korrekt mit expliziter Typspezifikation. Niemand benutzt IDE? Jeder benutzt nur int / float / bool Variablen? Jeder bevorzugt externe Dokumentation für Bibliotheken anstelle von selbst dokumentierten Kopfzeilen?
Avtomaton

that GotW: herbSutter.com/2013/08/12/… Ich verstehe nicht, dass diese "initializer_list surprise" eine Überraschung ist. Klammern um =RHS machen bei keiner anderen Interpretation viel Sinn (Init-Liste in Klammern , aber Sie müssen wissen, was Sie initialisieren, was ein Oxymoron ist auto). Die eine , die ist überraschend ist auto i{1}auch herzuleiten initializer_list, trotz damit nicht sagen , nehmen diese verspannt init-Liste , sondern diesen Ausdruck nehmen und seine Art verwenden ... aber wir bekommen initializer_listes auch. Zum Glück behebt C ​​++ 17 das alles gut.
Underscore_d

112

Es ist eine Situation von Fall zu Fall.

Manchmal ist Code schwieriger zu verstehen, manchmal nicht. Nimm zum Beispiel:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

ist definitiv leicht zu verstehen und definitiv leichter zu schreiben als die eigentliche Iterator-Deklaration.

Ich benutze C ++ schon eine Weile, aber ich kann garantieren, dass ich bei meinem ersten Versuch einen Compilerfehler bekomme, weil ich das vergesse const_iteratorund mich anfangs für das entscheiden würde iterator... :)

Ich würde es für solche Fälle verwenden, aber nicht dort, wo es den Typ tatsächlich verschleiert (wie Ihre Situation), aber dies ist rein subjektiv.


45
Genau. Wen zum Teufel interessiert der Typ. Es ist ein Iterator. Der Typ ist mir egal, ich muss nur wissen, dass ich ihn zum Iterieren verwenden kann.
R. Martinho Fernandes

5
+1. Selbst wenn Sie den Typ benannt haben, würden Sie ihn so benennen std::map<int, std::string>::const_iterator, sodass der Name Ihnen sowieso nicht viel über den Typ verrät.
Steve Jessop

4
@SteveJessop: Es sagt mir mindestens zwei Dinge: Der Schlüssel ist intund der Wert ist std::string. :)
Nawaz

16
@Nawaz: und das kann man nicht zuordnen, it->secondda es ein const iterator ist. Alle diese Informationen sind Wiederholungen der vorherigen Zeile const std::map<int, std::string>& x. Wenn Sie mehrmals Dinge sagen, ist dies gelegentlich eine bessere Information, aber dies ist keineswegs eine allgemeine Regel :-)
Steve Jessop

11
TBH Ich würde es vorziehen for (anX : x), noch deutlicher zu machen, dass wir nur darüber nachdenken x. Der Normalfall, in dem Sie einen Iterator benötigen, ist, wenn Sie den Container xconst&
ändern

94

Sieh es anders an. Schreibst du:

std::cout << (foo() + bar()) << "\n";

oder:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

Manchmal hilft es nicht, den Typ explizit zu buchstabieren.

Die Entscheidung, ob Sie den Typ angeben müssen, stimmt nicht mit der Entscheidung überein, ob Sie den Code durch Definieren von Zwischenvariablen auf mehrere Anweisungen aufteilen möchten. In C ++ 03 wurden die beiden verknüpft. Sie können sich vorstellen, autowie Sie sie trennen können.

Manchmal kann es nützlich sein, die Typen explizit anzugeben:

// seems legit    
if (foo() < bar()) { ... }

gegen

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

In Fällen, in denen Sie eine Variable deklarieren, autolässt using den Typ unausgesprochen, so wie er in vielen Ausdrücken vorkommt. Sie sollten wahrscheinlich selbst entscheiden, wann dies die Lesbarkeit verbessert und wann es behindert.

Sie können argumentieren, dass das Mischen von vorzeichenbehafteten und nicht vorzeichenbehafteten Typen zunächst ein Fehler ist (einige argumentieren außerdem, dass man überhaupt keine nicht vorzeichenbehafteten Typen verwenden sollte). Der Grund , warum es wohl ein Fehler ist, ist, dass die Typen der Operanden aufgrund des unterschiedlichen Verhaltens von entscheidender Bedeutung sind. Wenn es schlecht ist, die Typen Ihrer Werte zu kennen, ist es wahrscheinlich auch nicht schlecht, sie nicht zu kennen. Vorausgesetzt, der Code ist nicht bereits aus anderen Gründen verwirrend, macht das autoOK, oder? ;-)

Insbesondere beim Schreiben von generischem Code gibt es Fälle, in denen der tatsächliche Typ einer Variablen nicht wichtig sein sollte. Entscheidend ist jedoch, dass die erforderliche Schnittstelle erfüllt wird. So autobietet eine Abstraktionsebene , wo Sie die Art ignorieren (aber natürlich nicht der Compiler nicht, weiß es). Wenn Sie auf einer geeigneten Abstraktionsebene arbeiten, kann dies die Lesbarkeit erheblich verbessern. Wenn Sie auf einer "falschen" Ebene arbeiten, wird das Lesen des Codes zu einem Problem.


21
Mit +1 autokönnen Sie benannte Variablen mit unbenennbaren oder uninteressanten Typen erstellen. Sinnvolle Namen können nützlich sein.
Mankarse

Mischen von vorzeichenbehaftet und vorzeichenlos, wenn Sie vorzeichenlos für die richtige Verwendung verwenden: modulare Arithmetik. Dies ist nicht der Fall, wenn Sie unsigned for positive integer missbrauchen. Kaum ein Programm hat eine Verwendung für unsigned, aber die Kernsprache erzwingt die inane Definition, dass sizeofSie unsigned sind.
neugierig

27

IMO, Sie sehen das so ziemlich umgekehrt.

Es geht nicht darum auto, zu Code zu führen, der nicht lesbar oder noch weniger lesbar ist. Es ist eine Frage der Hoffnung, dass ein expliziter Typ für den Rückgabewert die Tatsache ausgleichen wird, dass (anscheinend) nicht klar ist, welcher Typ von einer bestimmten Funktion zurückgegeben wird.

Zumindest ist das nach meiner Meinung Ihr Problem, wenn Sie eine Funktion haben, deren Rückgabetyp nicht sofort offensichtlich ist. Was die Funktion tut, sollte aus ihrem Namen ersichtlich sein, und der Typ des Rückgabewerts sollte aus dem, was sie tut, ersichtlich sein. Wenn nicht, ist das die eigentliche Ursache des Problems.

Wenn es hier ein Problem gibt, ist es nicht mit auto. Es ist mit dem Rest des Codes verbunden, und die Chancen stehen gut, dass der explizite Typ nur als Hilfsmittel ausreicht, um Sie davon abzuhalten, das Kernproblem zu sehen und / oder zu beheben. Sobald Sie dieses echte Problem behoben haben, ist die Lesbarkeit des verwendeten Codes autoim Allgemeinen in Ordnung.

Ich nehme fairerweise an, ich sollte hinzufügen: Ich habe einige Fälle behandelt, in denen solche Dinge bei weitem nicht so offensichtlich waren, wie Sie möchten, und das Problem zu beheben war auch ziemlich unhaltbar. Zum Beispiel habe ich vor ein paar Jahren eine Beratung für ein Unternehmen durchgeführt, das zuvor mit einem anderen Unternehmen fusioniert war. Sie endeten mit einer Codebasis, die mehr "zusammengeschoben" als wirklich zusammengeführt wurde. Die konstituierenden Programme hatten damit begonnen, verschiedene (aber recht ähnliche) Bibliotheken für ähnliche Zwecke zu verwenden, und obwohl sie daran arbeiteten, die Dinge sauberer zusammenzuführen, taten sie dies immer noch. In einer ganzen Reihe von Fällen war die einzige Möglichkeit, zu erraten, welcher Typ von einer bestimmten Funktion zurückgegeben wurde, zu wissen, wo diese Funktion entstanden war.

Selbst in einem solchen Fall können Sie dazu beitragen, einige Dinge klarer zu machen. In diesem Fall begann der gesamte Code im globalen Namespace. Das einfache Verschieben einer angemessenen Menge in einige Namespaces beseitigte die Namenskonflikte und erleichterte auch das Typ-Tracking.


17

Es gibt mehrere Gründe, warum ich Auto für den allgemeinen Gebrauch nicht mag:

  1. Sie können den Code umgestalten, ohne ihn zu ändern. Ja, dies ist eines der Dinge, die häufig als Vorteil der Verwendung von auto aufgeführt werden. Ändern Sie einfach den Rückgabetyp einer Funktion, und wenn der gesamte aufgerufene Code auto verwendet, ist kein zusätzlicher Aufwand erforderlich! Wenn Sie auf compile klicken, wird es erstellt - 0 Warnungen, 0 Fehler - und Sie checken einfach Ihren Code ein, ohne sich mit dem Durchsuchen und potenziellen Ändern der 80 Stellen befassen zu müssen, an denen die Funktion verwendet wird.

Aber warte, ist das wirklich eine gute Idee? Was wäre, wenn der Typ in einem halben Dutzend dieser Anwendungsfälle eine Rolle spielen würde und sich dieser Code nun tatsächlich anders verhält? Dies kann auch implizit die Kapselung unterbrechen, indem nicht nur die Eingabewerte, sondern auch das Verhalten der privaten Implementierung anderer Klassen, die die Funktion aufrufen, geändert werden.

1a. Ich glaube an das Konzept des "selbstdokumentierenden Codes". Der Grund für den selbstdokumentierenden Code ist, dass Kommentare dazu neigen, nicht mehr aktuell zu sein, was der Code tut, während der Code selbst - wenn er explizit geschrieben ist - selbsterklärend ist und immer auf dem neuesten Stand bleibt auf seine Absicht und wird Sie nicht mit abgestandenen Kommentaren verwechseln lassen. Wenn Typen geändert werden können, ohne dass der Code selbst geändert werden muss, können der Code / die Variablen selbst veralten. Zum Beispiel:

auto bThreadOK = CheckThreadHealth ();

Das Problem besteht jedoch darin, dass CheckThreadHealth () zu einem bestimmten Zeitpunkt überarbeitet wurde, um anstelle eines Bools einen Aufzählungswert zurückzugeben, der den Fehlerstatus angibt, sofern vorhanden. Die Person, die diese Änderung vorgenommen hat, hat es jedoch versäumt, diese bestimmte Codezeile zu überprüfen, und der Compiler war nicht hilfreich, da er ohne Warnungen oder Fehler kompiliert wurde.

  1. Sie können nie wissen, was die tatsächlichen Typen sind. Dies wird auch häufig als primärer "Vorteil" von auto aufgeführt. Warum lernen Sie, was eine Funktion Ihnen gibt, wenn Sie einfach sagen können: "Wen interessiert das? Sie kompiliert!"

Es funktioniert wahrscheinlich sogar irgendwie. Ich sage Art von Arbeit, denn obwohl Sie eine Kopie einer 500-Byte-Struktur für jede Schleifeniteration erstellen, können Sie einen einzelnen Wert darauf überprüfen, der Code ist dennoch voll funktionsfähig. Selbst Ihre Komponententests helfen Ihnen nicht zu erkennen, dass sich hinter diesem einfachen und unschuldig aussehenden Auto schlechter Code verbirgt. Die meisten anderen Personen, die die Datei durchsuchen, bemerken dies auch nicht auf den ersten Blick.

Dies kann auch noch schlimmer gemacht werden, wenn Sie den Typ nicht kennen, aber einen Variablennamen wählen, der eine falsche Annahme darüber macht, was er ist, und tatsächlich dasselbe Ergebnis wie in 1a erzielt, jedoch nicht von Anfang an Post-Refactor.

  1. Das Eingeben des Codes beim ersten Schreiben ist nicht der zeitaufwändigste Teil der Programmierung. Ja, Auto beschleunigt anfangs das Schreiben von Code. Als Haftungsausschluss gebe ich> 100 WPM ein. Vielleicht stört es mich nicht so sehr wie andere. Aber wenn ich nur den ganzen Tag neuen Code schreiben müsste, wäre ich ein glücklicher Camper. Der zeitaufwändigste Teil der Programmierung besteht in der Diagnose schwer reproduzierbarer Fehler in Groß- und Kleinschreibung im Code, die häufig auf subtile, nicht offensichtliche Probleme zurückzuführen sind, wie z. signiert vs. unsigniert, float vs. int, bool vs. pointer, etc.).

Es scheint mir offensichtlich, dass auto in erster Linie als Problemumgehung für schreckliche Syntax mit Standardvorlagentypen für Bibliotheken eingeführt wurde. Anstatt zu versuchen, die Schablonensyntax zu korrigieren, mit der die Benutzer bereits vertraut sind - was aufgrund des vorhandenen Codes, der möglicherweise beschädigt wird, auch fast unmöglich ist -, fügen Sie ein Schlüsselwort hinzu, das das Problem im Grunde verbirgt. Im Wesentlichen, was man als "Hack" bezeichnen könnte.

Ich bin mit der Verwendung von auto für Standard-Bibliothekscontainer eigentlich nicht einverstanden. Es ist offensichtlich, wofür das Schlüsselwort erstellt wurde, und es ist unwahrscheinlich, dass Funktionen in der Standardbibliothek ihren Zweck (oder Typ) grundlegend ändern, was die Verwendung von auto relativ sicher macht. Ich wäre jedoch sehr vorsichtig, wenn ich es mit Ihrem eigenen Code und Ihren Schnittstellen verwenden würde, die möglicherweise sehr viel unbeständiger sind und möglicherweise grundlegenderen Änderungen unterliegen.

Eine weitere nützliche Anwendung von auto, mit der die Sprachfunktionen verbessert werden, ist das Erstellen von Temporären in typunabhängigen Makros. Das ist etwas, was du vorher nicht wirklich tun konntest, aber du kannst es jetzt tun.


4
Du hast den Nagel auf den Kopf getroffen. Ich wünschte, ich könnte dies eine +2 geben.
cmaster

Eine gute "verdammt vorsichtige" Antwort. @cmaster: Da ist es.
Deduplizierer

Ich habe eine nützlichen Fall gefunden: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist

14

Ja, es macht es einfacher, den Typ Ihrer Variablen zu kennen, wenn Sie ihn nicht verwenden auto. Die Frage ist: Haben Sie müssen den Typ Ihrer Variablen kennen , den Code zu lesen? Manchmal lautet die Antwort ja, manchmal nein. Wenn Sie beispielsweise einen Iterator von einem abrufen std::vector<int>, müssen Sie wissen, dass dies ein std::vector<int>::iteratoroder auto iterator = ...;ausreichend ist? Alles, was jemand mit einem Iterator machen möchte, ist durch die Tatsache gegeben, dass es sich um einen Iterator handelt - es spielt nur keine Rolle, um welchen Typ es sich handelt.

Verwenden autoSie diese Option in Situationen, in denen das Lesen des Codes nicht erschwert wird.


12

Persönlich benutze ich autonur, wenn es für den Programmierer absolut offensichtlich ist, was es ist.

Beispiel 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Beispiel 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

5
Dies ist ein klares Beispiel für schlechte Lesbarkeit, nicht wirklich automatisch. Niemand hat die geringste Ahnung, was "DoSomethingWeird" macht. Wenn Sie also "Auto" verwenden oder nicht, wird es nicht lesbarer. Sie müssen die Dokumente in beiden Fällen überprüfen.
R. Martinho Fernandes

4
Ok, jetzt ist es etwas besser. Ich finde immer noch, dass die Variable schlecht benannt ist, was immer noch weh tut. Würden Sie schreiben auto record = myObj.FindRecord(something), wäre klar, dass der Variablentyp ein Datensatz war. Oder itwenn Sie es benennen oder ähnliches, wird deutlich, dass es einen Iterator zurückgibt. Beachten Sie, dass Sie, selbst wenn Sie autodie Variable nicht verwenden , bei korrekter Benennung nicht zur Deklaration zurückkehren müssen, um den Typ von einer beliebigen Stelle in der Funktion aus zu betrachten . Ich habe meine Ablehnung entfernt, weil das Beispiel jetzt kein vollständiger Strohmann ist, aber ich kaufe das Argument hier immer noch nicht.
R. Martinho Fernandes

2
@R.MartinhoFernandes hinzufügen: Die Frage ist, ist es jetzt wirklich wichtig, WAS ein "Datensatz" genau ist? Es ist meiner Meinung nach wichtiger, dass es sich um einen Datensatz handelt, der eigentliche zugrunde liegende Primitivtyp ist eine weitere Abstraktionsebene. Ohne Auto hätte man also wahrscheinlich:MyClass::RecordTy record = myObj.FindRecord (something)
paul23

2
@ paul23: Was bringt dir die Verwendung von Auto im Vergleich zum Typ, wenn du nur den Einwand hast "Ich weiß nicht, wie ich das verwenden soll". Entweder lässt du es trotzdem nachschlagen.
GManNickG

3
@GManNickG es sagt mir die genaue Art der Unwichtigkeit.
Paul23

10

Diese Frage erfordert eine Meinung, die von Programmierer zu Programmierer unterschiedlich sein wird, aber ich würde nein sagen. In vielen Fällen kann genau das Gegenteil autodazu beitragen, den Code verständlicher zu machen, indem der Programmierer sich auf die Logik und nicht auf die Minutien konzentriert.

Dies gilt insbesondere angesichts komplexer Vorlagentypen. Hier ist ein vereinfachtes & ausgedachtes Beispiel. Was ist leichter zu verstehen?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. oder...

for( auto it = things_.begin(); it != things_.end(); ++it )

Einige würden sagen, die zweite ist leichter zu verstehen, andere mögen die erste sagen. Wieder andere könnten sagen, dass eine unbegründete Verwendung von autodazu beitragen kann, die Programmierer, die es verwenden, zu beschönigen, aber das ist eine andere Geschichte.


4
+1 Haha, jeder präsentiert std::mapBeispiele, zusätzlich mit komplexen Template-Argumenten.
Nawaz

1
@Nawaz: Es ist einfach, mit maps verrückte lange Vorlagennamen zu finden . :)
John Dibling

@Nawaz: aber ich frage mich, warum dann niemand mit Range Based for Loops als die bessere und besser lesbare Alternative kommt ...
PlasmaHH

1
@PlasmaHH, nicht alle Schleifen mit Iteratoren können durch bereichsbasierte ersetzt werden, forz. B. wenn Iteratoren im Schleifenkörper ungültig sind und daher vorab oder gar nicht inkrementiert werden müssen.
Jonathan Wakely

@PlasmaHH: In meinem Fall ist MSVC10 nicht bereichsbezogen für Schleifen. Da MSVC10 mein Go-to-C ++ 11-Testbed ist, habe ich nicht wirklich viel Erfahrung mit ihnen.
John Dibling

8

Bisher viele gute Antworten, aber um sich auf die ursprüngliche Frage zu konzentrieren, denke ich, dass Herb in seinen Ratschlägen zu weit geht, um sie autogroßzügig zu verwenden . Ihr Beispiel ist ein Fall, in dem die Verwendung autooffensichtlich die Lesbarkeit beeinträchtigt. Einige Leute bestehen darauf, dass es bei modernen IDEs kein Problem gibt, bei denen Sie den Mauszeiger über eine Variable halten und den Typ sehen können, aber ich bin anderer Meinung: Selbst Leute, die immer eine IDE verwenden, müssen manchmal Codeausschnitte isoliert betrachten (denken Sie an Codeüberprüfungen) zum Beispiel) und eine IDE wird nicht helfen.

Fazit: Verwenden autoSie diese Option, wenn dies hilfreich ist: Iteratoren in for-Schleifen. Verwenden Sie es nicht, wenn der Leser Schwierigkeiten hat, den Typ herauszufinden.


6

Ich bin ziemlich überrascht, dass noch niemand darauf hingewiesen hat, dass Auto hilft, wenn es keinen eindeutigen Typ gibt. In diesem Fall umgehen Sie dieses Problem entweder, indem Sie in einer Vorlage ein #define oder ein typedef verwenden, um den tatsächlich verwendbaren Typ zu finden (und dies ist manchmal nicht trivial), oder Sie verwenden einfach auto.

Angenommen, Sie haben eine Funktion, die etwas mit plattformspezifischem Typ zurückgibt:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Würdest du lieber eine Hexe benutzen?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

oder einfach nur

auto stuff = getStuff();

Klar, du kannst schreiben

#define StuffType (...)

auch irgendwo, tut es aber

StuffType stuff = getStuff();

eigentlich noch was über xs typ erzählen? Es sagt, es ist das, was von dort zurückgegeben wird, aber es ist genau das, was auto ist. Das ist einfach überflüssig - 'Zeug' wird hier dreimal geschrieben - dies macht es meiner Meinung nach weniger lesbar als die 'Auto'-Version.


5
Der richtige Umgang mit plattformspezifischen Typen ist für typedefsie.
cmaster

3

Die Lesbarkeit ist subjektiv. Sie müssen sich die Situation ansehen und entscheiden, was am besten ist.

Wie Sie bereits betont haben, können lange Deklarationen ohne Auto viel Durcheinander erzeugen. Aber wie Sie auch betont haben, können kurze Deklarationen wertvolle Typinformationen entfernen.

Darüber hinaus möchte ich Folgendes hinzufügen: Achten Sie darauf, dass Sie auf Lesbarkeit und nicht auf Beschreibbarkeit achten. Code, der einfach zu schreiben ist, ist im Allgemeinen nicht einfach zu lesen und umgekehrt. Wenn ich zum Beispiel schreibe, bevorzuge ich auto. Wenn ich lese, vielleicht die längeren Erklärungen.

Dann gibt es Konsistenz; wie wichtig ist dir das Möchten Sie Auto in einigen Teilen und explizite Deklarationen in anderen oder eine durchgängig einheitliche Methode?


2

Ich werde den Vorteil von weniger lesbarem Code nutzen und den Programmierer ermutigen, ihn mehr und mehr zu verwenden. Warum? Wenn der Code, der auto verwendet, schwer zu lesen ist, wird es natürlich auch schwierig sein, ihn zu schreiben. Der Programmierer ist gezwungen, den aussagekräftigen Variablennamen zu verwenden , um seine Arbeit zu verbessern.
Vielleicht schreibt der Programmierer am Anfang nicht die aussagekräftigen Variablennamen. Aber irgendwann, während er die Fehler behebt oder in der Codeüberprüfung anderen den Code erklären muss oder in nicht allzu naher Zukunft, wird der Programmierer den Fehler erkennen und verwenden der aussagekräftige Variablenname in Zukunft.


2
Am besten lassen Sie die Leute Variablennamen schreiben myComplexDerivedType, um den fehlenden Typ auszugleichen, der den Code durch die Typwiederholung überfrachtet (überall, wo die Variable verwendet wird) und die Leute dazu verleitet, den Zweck der Variablen in ihrem Namen wegzulassen . Ich habe die Erfahrung gemacht, dass nichts so unproduktiv ist, als aktiv Hindernisse in den Code zu setzen.
cmaster

2

Ich habe zwei Richtlinien:

  • Wenn der Typ der Variablen offensichtlich ist, ist das Schreiben mühsam oder die Verwendung von auto schwer zu bestimmen.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • Wenn Sie eine bestimmte Konvertierung benötigen oder der Ergebnistyp nicht offensichtlich ist und Verwirrung stiften kann.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious

0

Ja. Es verringert die Ausführlichkeit, aber das übliche Missverständnis ist, dass die Ausführlichkeit die Lesbarkeit verringert. Dies gilt nur, wenn Sie die Lesbarkeit als ästhetisch und nicht als tatsächliche Fähigkeit zur Interpretation von Code betrachten - was durch die Verwendung von auto nicht erhöht wird. In dem am häufigsten zitierten Beispiel, Vektoriteratoren, kann es auf der Oberfläche erscheinen, dass die Verwendung von auto die Lesbarkeit Ihres Codes erhöht. Auf der anderen Seite wissen Sie nicht immer, was das automatische Schlüsselwort Ihnen geben wird. Sie müssen denselben logischen Pfad wie der Compiler einhalten, um diese interne Rekonstruktion durchzuführen, und in vielen Fällen, insbesondere bei Iteratoren, werden Sie die falschen Annahmen treffen.

Am Ende des Tages opfert 'auto' die Lesbarkeit von Code und Klarheit für syntaktische und ästhetische 'Sauberkeit' (was nur notwendig ist, weil Iteratoren eine unnötig verschlungene Syntax haben) und die Fähigkeit, 10 Zeichen weniger in eine bestimmte Zeile einzugeben. Es ist weder das Risiko noch den langfristigen Aufwand wert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.