Ich wechsle von C ++ 98 zu C ++ 11 und habe mich mit dem auto
Schlüsselwort vertraut gemacht . Ich habe mich gefragt, warum wir explizit deklarieren müssen, auto
ob der Compiler den Typ automatisch ableiten kann. Ich weiß, dass C ++ eine stark typisierte Sprache ist und dies eine Regel ist. War es jedoch nicht möglich, dasselbe Ergebnis zu erzielen, ohne eine Variable explizit zu deklarieren auto
?
80
Antworten:
Das Löschen des Expliziten
auto
würde die Sprache beschädigen :z.B
int main() { int n; { auto n = 0; // this shadows the outer n. } }
wo Sie sehen können, dass das Fallenlassen das Äußere
auto
nicht beschatten würden
.quelle
n := 0
eine neue Variable einführen. Warum verwendetauto
wird, ist eine meinungsbasierte Frage.:=
) eingeführt werden müssen. (d) Es passt bereits in die Grammatik. Ich denke, hier gibt es sehr wenig Raum für Meinungen.x = f()
eine neue Variable deklarieren (falls noch nicht vorhanden), um den Typ des Rückgabewerts von f zu erhalten. Wenn Sie automatisch eine Variable explizit deklarieren, verringert sich jedoch das Risiko, neue Variablen zu deklarieren aus Versehen (zB wegen eines Tippfehlers ...).Ihre Frage erlaubt zwei Interpretationen:
Bathseba antwortete gut auf die erste Interpretation, für die zweite auf Folgendes (vorausgesetzt, es existieren noch keine anderen Erklärungen; hypothetisch gültiges C ++):
int f(); double g(); n = f(); // declares a new variable, type is int; d = g(); // another new variable, type is double if(n == d) { n = 7; // reassigns n auto d = 2.0; // new d, shadowing the outer one }
Es wäre möglich, dass andere Sprachen recht gut zurechtkommen (abgesehen von dem Shadowing-Problem vielleicht) ... In C ++ ist dies jedoch nicht der Fall, und die Frage (im Sinne der zweiten Interpretation) lautet nun: Warum ?
Diesmal ist die Antwort nicht so offensichtlich wie in der ersten Interpretation. Eines ist jedoch offensichtlich: Die explizite Anforderung des Schlüsselworts macht die Sprache sicherer (ich weiß nicht, ob dies das Sprachkomitee zu seiner Entscheidung geführt hat, dennoch bleibt es ein Punkt):
grummel = f(); // ... if(true) { brummel = f(); //^ uh, oh, a typo... }
Können wir uns darauf einigen, dass wir keine weiteren Erklärungen benötigen?
(Zitierter Kommentar von psmears wegen seiner Wichtigkeit - danke für den Hinweis auf)
quelle
auto
meiner Ansicht nach darin, dass das Hinzufügen einer globalen Variablen an einer Stelle, die weit von einer Funktion entfernt ist (z. B. in einer Header-Datei), die Deklaration eines lokal begrenzten Bereichs ändern kann Variable in dieser Funktion in eine Zuordnung zur globalen Variablen ... mit möglicherweise katastrophalen (und sicherlich sehr verwirrenden) Folgen.global <variable>
Anweisung zu benötigen .) Dies würde natürlich noch mehr Änderungen an der C ++ - Sprache erfordern und wäre daher wahrscheinlich nicht möglich.MOV R6 R5
SUB #nnn R6
auf einem PDP-11 unter der Annahme, dass R5 als Rahmenzeiger und R6 als Stapelzeiger verwendet wird. nnn ist die Anzahl der benötigten Speicherbytes.Ich werde Ihre Frage leicht umformulieren, damit Sie verstehen, warum Sie Folgendes benötigen
auto
:War es nicht möglich ? Natürlich war es "möglich". Die Frage ist, ob sich die Mühe lohnt, dies zu tun.
Die meisten Syntaxen in anderen Sprachen, die keine Typnamen haben, funktionieren auf zwei Arten. Es gibt den Go-ähnlichen Weg, bei dem
name := value;
eine Variable deklariert wird. Und es gibt die Python-ähnliche Methode, bei dername = value;
eine neue Variable deklariert wird, wennname
sie zuvor nicht deklariert wurde.Nehmen wir an, dass es keine syntaktischen Probleme beim Anwenden einer der beiden Syntax auf C ++ gibt (obwohl ich bereits sehen kann, dass in C ++
identifier
gefolgt von:
"make a label" bedeutet). Was verlieren Sie also im Vergleich zu Platzhaltern?Nun, ich kann das nicht mehr tun:
auto &name = get<0>(some_tuple);
Siehe,
auto
bedeutet immer "Wert". Wenn Sie eine Referenz erhalten möchten, müssen Sie explizit a verwenden&
. Und es wird zu Recht nicht kompiliert, wenn der Zuweisungsausdruck ein Wert ist. Keine der zuweisungsbasierten Syntaxen kann zwischen Referenzen und Werten unterscheiden.Sie können jetzt festlegen, dass solche Zuweisungssyntaxen Referenzen ableiten, wenn der angegebene Wert eine Referenz ist. Aber das würde bedeuten, dass Sie nicht tun können:
auto name = get<0>(some_tuple);
Dies kopiert aus dem Tupel und erstellt ein Objekt unabhängig von
some_tuple
. Manchmal ist es genau das, was Sie wollen. Dies ist umso nützlicher, wenn Sie sich vom Tupel mit entfernen möchtenauto name = get<0>(std::move(some_tuple));
.OK, vielleicht könnten wir diese Syntax ein wenig erweitern, um diese Unterscheidung zu berücksichtigen. Vielleicht
&name := value;
oder&name = value;
würde bedeuten, eine Referenz wie abzuleitenauto&
.OK, gut. Was ist damit:
decltype(auto) name = some_thing();
Oh, das stimmt; C ++ hat tatsächlich zwei Platzhalter:
auto
unddecltype(auto)
. Die Grundidee dieses Abzugs ist, dass es genau so funktioniert, als ob Sie es getan hättendecltype(expr) name = expr;
. Wennsome_thing()
es sich in unserem Fall um ein Objekt handelt, wird daraus ein Objekt abgeleitet. Wennsome_thing()
es sich um eine Referenz handelt, wird eine Referenz abgeleitet.Dies ist sehr nützlich, wenn Sie im Vorlagencode arbeiten und nicht genau wissen, wie hoch der Rückgabewert einer Funktion sein wird. Dies ist ideal für die Weiterleitung und ein wesentliches Werkzeug, auch wenn es nicht weit verbreitet ist.
Jetzt müssen wir unsere Syntax erweitern.
name ::= value;
bedeutet "mach wasdecltype(auto)
macht". Ich habe kein Äquivalent für die Pythonic-Variante.Ist es bei dieser Syntax nicht einfach, versehentlich falsch zu tippen? Nicht nur das, es ist kaum selbstdokumentierend. Selbst wenn Sie es noch nie gesehen
decltype(auto)
haben, ist es groß und offensichtlich genug, dass Sie zumindest leicht erkennen können, dass etwas Besonderes los ist. Während der visuelle Unterschied zwischen::=
und:=
minimal ist.Aber das ist Meinungsmaterial; Es gibt wesentlichere Fragen. All dies basiert auf der Verwendung der Zuweisungssyntax. Nun ... was ist mit Orten, an denen Sie die Zuweisungssyntax nicht verwenden können? So was:
for(auto &x : container)
Ändern wir das in
for(&x := container)
? Denn das scheint etwas zu sagen , sehr verschieden von bereichsbasiertenfor
. Es sieht so aus, als wäre es die Initialisiereranweisung einer regulärenfor
Schleife, keine bereichsbasiertefor
. Es wäre auch eine andere Syntax als bei nicht abgeleiteten Fällen.Außerdem ist die Kopierinitialisierung (mit
=
) in C ++ nicht dasselbe wie die Direktinitialisierung (mit Konstruktorsyntax). Funktioniert alsoname := value;
möglicherweise nicht in Fällen, in denenauto name(value)
dies der Fall wäre.Sicher, Sie könnten deklarieren, dass
:=
die Direktinitialisierung verwendet wird, aber das wäre ziemlich inkongruent mit dem Verhalten von C ++.Außerdem gibt es noch eine Sache: C ++ 14. Es gab uns eine nützliche Abzugsfunktion: den Abzug vom Rückgabetyp. Dies basiert jedoch auf Platzhaltern.
for
Ähnlich wie bei einem Bereich basiert es im Wesentlichen auf einem Typnamen, der vom Compiler ausgefüllt wird, und nicht auf einer Syntax, die auf einen bestimmten Namen und Ausdruck angewendet wird.Alle diese Probleme stammen aus derselben Quelle: Sie erfinden eine völlig neue Syntax zum Deklarieren von Variablen. Platzhalterbasierte Deklarationen mussten keine neue Syntax erfinden . Sie verwenden genau die gleiche Syntax wie zuvor. Sie verwenden lediglich ein neues Schlüsselwort, das sich wie ein Typ verhält, aber eine besondere Bedeutung hat. Dies ermöglicht es ihm, im bereichsbezogenen
for
und für den Rückzugsartabzug zu arbeiten. Es ist das, was es erlaubt, mehrere Formen zu haben (auto
vs.decltype(auto)
). Und so weiter.Platzhalter funktionieren, weil sie die einfachste Lösung für das Problem darstellen und gleichzeitig alle Vorteile und die Allgemeingültigkeit der Verwendung eines tatsächlichen Typnamens beibehalten. Wenn Sie eine andere Alternative gefunden haben, die so universell funktioniert wie Platzhalter, ist es höchst unwahrscheinlich, dass sie so einfach wie Platzhalter ist.
Es sei denn, es wurden nur Platzhalter mit unterschiedlichen Schlüsselwörtern oder Symbolen geschrieben ...
quelle
auto
Deklarationen / Rückgabewertabzug hat.Kurz gesagt:
auto
könnte in einigen Fällen fallengelassen werden, aber das würde zu Inkonsistenzen führen.Wie bereits erwähnt, lautet die Deklarationssyntax in C ++ zunächst einmal
<type> <varname>
. Explizite Deklarationen erfordern an ihrer Stelle einen Typ oder zumindest ein Deklarationsschlüsselwort. Wir könnten alsovar <varname>
oderdeclare <varname>
oder etwas verwenden, ist aberauto
ein langjähriges Schlüsselwort in C ++ und ein guter Kandidat für das Schlüsselwort für den automatischen Typabzug.Ist es möglich, Variablen implizit durch Zuweisung zu deklarieren, ohne alles zu beschädigen?
Manchmal ja. Sie können keine Zuweisung außerhalb von Funktionen ausführen, daher können Sie dort die Zuweisungssyntax für Deklarationen verwenden. Ein solcher Ansatz würde jedoch zu Inkonsistenzen in der Sprache führen und möglicherweise zu menschlichen Fehlern führen.
a = 0; // Error. Could be parsed as auto declaration instead. int main() { return 0; }
Und wenn es um lokale Variablen geht, können explizite Deklarationen den Umfang einer Variablen steuern.
a = 1; // use a variable declared before or outside auto b = 2; // declare a variable here
Wenn mehrdeutige Syntax zulässig wäre, könnte das Deklarieren globaler Variablen plötzlich lokale implizite Deklarationen in Zuweisungen konvertieren. Um diese Conversions zu finden, müsste alles überprüft werden . Und um Kollisionen zu vermeiden, benötigen Sie eindeutige Namen für alle Globals, wodurch die gesamte Idee des Scoping zerstört wird. Es ist also wirklich schlimm.
quelle
auto
ist ein Schlüsselwort, das Sie an Stellen verwenden können, an denen Sie normalerweise einen Typ angeben müssen .int x = some_function();
Kann allgemeiner gestaltet werden, indem der
int
Typ automatisch abgeleitet wird:auto x = some_function();
Es ist also eine konservative Erweiterung der Sprache; es passt in die vorhandene Syntax. Ohne es
x = some_function()
wird eine Zuweisungserklärung, keine Erklärung mehr.quelle
Die Syntax muss eindeutig und auch abwärtskompatibel sein.
Wenn auto gelöscht wird, kann nicht zwischen Anweisungen und Definitionen unterschieden werden.
auto n = 0; // fine n=0; // statememt, n is undefined.
quelle
auto
es sich bereits um ein Schlüsselwort handelte (jedoch mit veralteter Bedeutung), sodass der Code nicht beschädigt wurde, wenn er als Name verwendet wurde. Dies ist ein Grund, warum stattdessen kein besseres Keyword wievar
oderlet
ausgewählt wurde.auto
ist eigentlich ein ziemlich gutes Schlüsselwort dafür: Es drückt genau das aus, wofür es steht, nämlich es ersetzt einen Typnamen durch "den automatischen Typ". Bei einem Schlüsselwort wievar
oderlet
sollten Sie folglich das Schlüsselwort auch dann benötigen , wenn der Typ explizit angegeben ist, dhvar int n = 0
oder so ähnlichvar n:Int = 0
. So wird es im Grunde in Rust gemacht.auto
im Kontext der vorhandenen Syntax definitiv hervorragend ist, würde ich sagen, dass so etwas wievar int x = 42
die grundlegende Variablendefinition mitvar x = 42
undint x = 42
als Kurzform sinnvoller wäre als die aktuelle Syntax, wenn sie aus historischem Inhalt betrachtet wird. Aber es ist meistens Geschmackssache. Aber Sie haben Recht, ich hätte "einen der Gründe" anstelle von "einen Grund" in meinen ursprünglichen Kommentarauto
, gibt es einen automatischen Typ (je nach Ausdruck einen anderen).Hinzu kommt eine zusätzliche Anmerkung zu einem alten Furz: Es sieht so aus, als ob Sie es als Vorteil ansehen, einfach eine neue Variable verwenden zu können, ohne sie in irgendeiner Weise zu deklarieren.
In Sprachen mit der Möglichkeit der impliziten Definition von Variablen kann dies ein großes Problem sein, insbesondere in größeren Systemen. Sie machen einen Tippfehler und debuggen stundenlang, nur um herauszufinden, dass Sie unbeabsichtigt eine Variable mit dem Wert Null (oder schlechter) eingeführt haben -
blue
vsbleu
,label
vslable
... das Ergebnis ist, dass Sie keinem Code wirklich vertrauen können, ohne genau zu prüfen, ob er genau ist Variablennamen.Nur mit
auto
wird sowohl dem Compiler als auch dem Betreuer mitgeteilt, dass Sie beabsichtigen, eine neue Variable zu deklarieren.Denken Sie darüber nach, um diese Art von Albträumen vermeiden zu können, wurde in FORTRAN die Aussage "implizit keine" eingeführt - und Sie sehen, dass sie heutzutage in allen seriösen FORTRAN-Programmen verwendet wird. Es nicht zu haben ist einfach ... beängstigend.
quelle