Frage Warum muss ich das Schlüsselwort "auto" explizit schreiben?


Ich gehe von C ++ 98 auf C ++ 11 zu und habe mich mit der auto Stichwort. Ich habe mich gefragt, warum wir das explizit erklären müssen auto wenn der Compiler den Typ automatisch ableiten kann. Ich weiß, dass C ++ eine stark typisierte Sprache ist, und dies ist eine Regel, aber war es nicht möglich, dasselbe Ergebnis zu erreichen, ohne explizit eine Variable zu deklarieren auto?


75
2018-05-23 08:15


Ursprung


Antworten:


Löschen des expliziten auto würde die Sprache brechen:

z.B.

int main()
{
    int n;
    {
        auto n = 0; // this shadows the outer n.
    }
}

wo du sehen kannst, dass das fallen lässt auto würde nicht Schatten das Äußere n.


154
2018-05-23 08:16



Ihre Frage lässt zwei Interpretationen zu:

  • Warum brauchen wir überhaupt "Auto"? Können wir es nicht einfach fallen lassen?
  • Warum sind wir verpflichtet, auto zu verwenden? Können wir es nicht implizit haben, wenn es nicht gegeben ist?

Bathsheba antwortete schön die erste Interpretation, für die zweite, bedenke folgendes (vorausgesetzt, es gibt bisher 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ürde möglich sein, andere Sprachen kommen ganz gut damit zurecht (naja, abgesehen von der Frage des Shadowings vielleicht) ... Es ist aber nicht so in C ++ und die Frage (im Sinne der zweiten Interpretation) ist nun: Warum?

Diesmal ist die Antwort nicht so offensichtlich wie in der ersten Interpretation. Eines ist jedoch offensichtlich: Die ausdrückliche Anforderung an das Schlüsselwort macht die Sprache sicherer (ich weiß nicht, ob dies das Sprachkomitee zu seiner Entscheidung trieb, es bleibt immer noch 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?

Die noch größere Gefahr, wenn man auto nicht benötigt, ist, dass das Hinzufügen einer globalen Variablen an einem Ort weit entfernt von einer Funktion (zB in einer Header-Datei) die Deklaration eines lokal Scope Variable in dieser Funktion in eine Zuordnung zu der globalen Variable ... mit potenziell katastrophalen (und sicherlich sehr verwirrend) Konsequenzen.

(zitiert Psmears ' Kommentar aufgrund seiner Wichtigkeit - Danke für Hinweise auf)


37
2018-05-23 08:47



war es nicht möglich, dasselbe Ergebnis zu erreichen, ohne explizit eine Variable zu deklarieren auto?

Ich werde Ihre Frage leicht so umformulieren, dass Sie verstehen, warum Sie sie brauchen auto:

War es nicht möglich, das gleiche Ergebnis ohne explizit zu erreichen? einen Typ-Platzhalter verwenden?

War es nicht möglich? Natürlich war es "möglich". Die Frage ist, ob es sich lohnt, es zu tun.

Die meisten Syntaxen in anderen Sprachen, die keine Namen eingeben, funktionieren auf zwei Arten. Da ist der Go-ähnliche Weg, wo name := value; deklariert eine Variable. Und da ist der Python-ähnliche Weg, wo name = value; deklariert eine neue Variable if name wurde noch nicht deklariert.

Angenommen, es gibt keine syntaktischen Probleme beim Anwenden einer der beiden Syntax auf C ++ (obwohl ich das schon sehen kann) identifier gefolgt von :in C ++ bedeutet "make a label"). Also, was verlierst du im Vergleich zu Platzhaltern?

Nun, ich kann das nicht mehr tun:

auto &name = get<0>(some_tuple);

Sehen, auto bedeutet immer "Wert". Wenn Sie eine Referenz erhalten möchten, müssen Sie explizit eine verwenden &. Und es wird zu Recht nicht kompilieren, wenn der Zuweisungsausdruck ein Pr-Wert ist. Keine der zuweisungsbasierten Syntaxen kann Referenzen und Werte unterscheiden.

Nun könnten Sie solche Zuweisungssyntaxen dazu bringen, Referenzen abzuleiten, wenn der angegebene Wert eine Referenz ist. Aber das würde bedeuten, dass du es nicht tun kannst:

auto name = get<0>(some_tuple);

Dies Kopien aus dem Tupel, Erstellen eines Objekts unabhängig von some_tuple. Manchmal ist das genau was du willst. Dies ist noch nützlicher, wenn Sie mit dem Tupel mit wechseln möchten auto name = get<0>(std::move(some_tuple));.

OK, vielleicht könnten wir diese Syntax etwas erweitern, um diese Unterscheidung zu berücksichtigen. Könnte sein &name := value; oder &name = value; würde bedeuten, eine Referenz wie folgt abzuleiten auto&.

OK, gut. Was ist damit?

decltype(auto) name = some_thing();

Oh, das stimmt; C ++ hat tatsächlich zwei Platzhalter: auto und decltype(auto). Der Grundgedanke dieser Schlussfolgerung ist, dass es genau so funktioniert, als ob Sie es getan hätten decltype(expr) name = expr;. Also in unserem Fall, wenn some_thing() Ist ein Objekt, wird daraus ein Objekt abgeleitet. Ob some_thing() ist eine Referenz, es wird eine Referenz abgeleitet.

Dies ist sehr nützlich, wenn Sie im Vorlagencode arbeiten und nicht genau wissen, wie der Rückgabewert einer Funktion aussehen wird. Dies ist ideal für die Weiterleitung, und es ist ein wesentliches Werkzeug, auch wenn es nicht weit verbreitet ist.

Jetzt müssen wir unserer Syntax mehr hinzufügen. name ::= value; bedeutet "tu was decltype(auto) tut ". Ich habe kein Äquivalent für die Pythonic Variante.

Wenn man sich diese Syntax anschaut, ist das nicht so einfach, versehentlich zu tippen? Nicht nur das, es ist kaum selbstdokumentierend. Selbst wenn du noch nie gesehen hast decltype(auto) Vorher ist es groß und offensichtlich genug, dass man zumindest leicht erkennen kann, dass etwas Besonderes passiert. Während der visuelle Unterschied zwischen ::= und := ist minimal.

Aber das ist Meinung Zeug; es gibt mehr inhaltliche Fragen. Sehen Sie, all dies basiert auf der Verwendung der Zuweisungssyntax. Nun ... was ist mit Orten, wo Sie kippen Verwenden Sie Zuordnungssyntax? So was:

for(auto &x : container)

Ändern wir das zu for(&x := container)? Weil das etwas zu sagen scheint sehr verschieden aus dem Bereich basiert for. Es sieht so aus, als wäre es die Initialisiereraussage eines Regulären for Schleife, keine bereichsbasierte for. Es wäre auch eine andere Syntax als nicht abgeleitete Fälle.

Auch Kopier-Initialisierung (mit =) ist in C ++ nicht das Gleiche wie direkte Initialisierung (mit Konstruktorsyntax). Damit name := value; funktioniert möglicherweise nicht in Fällen, in denen auto name(value) hätte.

Sicher, du könntest das erklären := wird Direct-Initialization verwenden, aber das wäre ziemlich inkongruent mit der Art, wie sich der Rest von C ++ verhält.

Außerdem gibt es noch eine Sache: C ++ 14. Es gab uns eine nützliche Deduktionsfunktion: Rückgabetyp Abzug. Dies basiert jedoch auf Platzhaltern. So ähnlich wie range-based forEs basiert im Wesentlichen auf einem Typnamen, der vom Compiler ausgefüllt wird, nicht durch eine 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. Platzhalter-basierte Deklarationen mussten nicht neu erfinden Syntax. Sie verwenden genau die gleiche Syntax wie zuvor; Sie verwenden nur ein neues Schlüsselwort, das sich wie ein Typ verhält, aber eine besondere Bedeutung hat. Dies ermöglicht es, bereichsbasiert zu arbeiten for und für den Rückgabetyp Abzug. Es ermöglicht, dass es mehrere Formen hat (auto gegen decltype(auto)). Und so weiter.

Platzhalter funktionieren, weil sie die einfachste Lösung für das Problem sind, während gleichzeitig alle Vorteile und die allgemeine Verwendung eines tatsächlichen Typnamens beibehalten werden. Wenn Sie eine andere Alternative gefunden haben, die so universell funktioniert wie Platzhalter, ist es sehr unwahrscheinlich, dass es so einfach ist wie Platzhalter.

Es sei denn, es wurden nur Platzhalter mit anderen Schlüsselwörtern oder Symbolen geschrieben ...


14
2018-05-24 02:32



Zusamenfassend: auto könnte in einigen Fällen fallen gelassen werden, aber das würde zu Inkonsistenz führen.

Zunächst einmal, wie gesagt, ist die Deklarationssyntax in C ++ <type> <varname>. Explizite Deklarationen erfordern an ihrer Stelle einen Typ oder zumindest ein Deklarationsschlüsselwort. So könnten wir verwenden var <varname> oder declare <varname> oder etwas, aber auto ist ein langjähriges Schlüsselwort in C ++ und ist ein guter Kandidat für das Keyword "automatisches Keyword".

Ist es möglich, Variablen implizit durch Zuweisung zu deklarieren, ohne alles zu brechen?

Manchmal ja. Sie können keine Zuweisung außerhalb von Funktionen durchführen, daher könnten 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 irgendeine Art von lokalen Variablen geht, sind explizite Deklarationen die Art, den Umfang einer Variablen zu kontrollieren.

a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here

Wenn eine mehrdeutige Syntax zulässig war, konnten bei der Deklaration globaler Variablen plötzlich implizite lokale Deklarationen in Zuweisungen konvertiert werden. Diese Conversions müssen überprüft werden alles. Und um Kollisionen zu vermeiden, würden Sie für alle Globals eindeutige Namen benötigen, die die gesamte Idee des Scoping zerstören. Es ist wirklich schlimm.


12
2018-05-23 13:55



auto ist ein Schlüsselwort, das Sie an Stellen verwenden können, an denen Sie normalerweise einen angeben müssen Art.

  int x = some_function();

Kann generischer gemacht werden, indem man das macht int Typ automatisch abgeleitet:

  auto x = some_function();

Es ist also eine konservative Erweiterung der Sprache; Es passt in die bestehende Syntax. Ohne es x = some_function() wird zu einer Zuweisungsanweisung, nicht mehr zu einer Deklaration.


11
2018-05-23 08:28



Syntax muss eindeutig und auch abwärtskompatibel sein.

Wenn Auto gelöscht wird, gibt es keine Möglichkeit, zwischen Anweisungen und Definitionen zu unterscheiden.

auto n = 0; // fine
n=0; // statememt, n is undefined.

9
2018-05-23 08:24



Zu den vorherigen Antworten eine zusätzliche Notiz von einem alten Furz hinzufügen: Es sieht so aus, als ob Sie es als einen Vorteil sehen könnten, einfach eine neue Variable zu verwenden, ohne sie in irgendeiner Weise zu deklarieren.

In Sprachen mit der Möglichkeit der impliziten Definition von Variablen kann dies insbesondere in größeren Systemen ein großes Problem sein. Sie machen einen Tippfehler und debuggen stundenlang, um herauszufinden, dass Sie versehentlich eine Variable mit einem Wert von Null (oder schlechter) eingeführt haben - blue vs bleu, label vs lable ... das Ergebnis ist, dass Sie keinen Code wirklich vertrauen können, ohne genaue Variablennamen zu überprüfen.

Einfach benutzen auto teilt Compiler und Maintainer mit, dass Sie eine neue Variable deklarieren möchten.

Denken Sie darüber nach, um diese Art von Albträumen zu vermeiden, wurde die "implizite none" -Aussage in FORTRAN eingeführt - und Sie sehen, dass sie heutzutage in allen ernsthaften FORTRAN-Programmen verwendet wird. Nicht zu haben ist einfach ... gruselig.


3
2018-05-30 00:03