Soll ich die neue C ++ 11-Funktion "Auto" verwenden, insbesondere in Schleifen?

20

Was sind die Vor- / Nachteile der auto Schlüsselworts, insbesondere bei for-Schleifen?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Wenn Sie nicht wissen, ob Sie einen Iterator für eine Karte oder einen Vektor haben, würden Sie wahrscheinlich nicht wissen, ob Sie ihn verwenden sollen firstsecond Eigenschaften des Objekts oder direkt darauf zugreifen sollen, nein?

Dies erinnert mich an die C # -Debatte über die Verwendung des Schlüsselworts var. Der Eindruck, den ich bisher habe, ist, dass die Leute in der C ++ - Welt bereit sind, das autoSchlüsselwort mit weniger Kampf als varin der C # -Welt zu übernehmen. Für mich ist mein erster Instinkt, dass ich gerne den Typ der Variablen kenne, damit ich weiß, welche Operationen ich erwarten kann, um sie auszuführen.

Benutzer
quelle
3
Warten! Es gab einen Kampf um die Verwendung var? Das habe ich vermisst.
pdr
21
Noch besser, Sie können einfach verwenden for (auto& it : x)(oder ohne Hinweis, wenn Sie kopieren möchten)
Tamás Szelei
7
Scheint mir, wenn Sie eine Schleife schreiben, um über den Inhalt von iterieren, xund Sie nicht einmal wissen, was xist, sollten Sie diese Schleife nicht an erster Stelle schreiben
;-)
@fish: bereichsbasierte For-Loops-Regeln, aber ich wäre pedantisch und erledigt gewesen: 'for (T & it: x)' stattdessen, wenn ich bereichsbasierte For-Loops verwende, da ich der Meinung bin, dass die Verwendung von Auto dort weniger informativ ist. Irgendwie ein Missbrauch von Auto in meinem Kopf.
Martiert
Der Streit um die Verwendung von var war ein wenig albern, vor allem im Nachhinein. Siehe Frachtkultprogrammierung .
Craig

Antworten:

38

Die Motivationen in C ++ sind extremer, da Typen aufgrund von Metaprogrammierung und anderen Dingen sehr viel komplexer und komplexer werden können als C # -Typen. autoist schneller zu schreiben und zu lesen und flexibler / wartbarer als ein expliziter Typ. Ich meine, möchtest du anfangen zu tippen?

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

Das ist nicht mal der volle Typ. Ich habe ein paar Template-Argumente verpasst.

DeadMG
quelle
8
+1 für das Beispiel, aber das sagt auch etwas über den Zustand von "modernem" C ++ aus.
zvrba
22
@zvrba: Ja, dass die generischen Funktionen viel leistungsfähiger sind als die von C #.
DeadMG
4
Dafür steht typedef
gbjbaanb
19
@ gbjbaanb Nein, das ist was autofür. Von Entwurf. typedefhilft, autohilft aber mehr.
Konrad Rudolph
1
typedef macht das Argument ungültig, dass "die Nichtverwendung von auto für sehr lange Typen sorgt"
Michael
28

In deinem Beispiel:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

es muss eine deklaration für xsichtbar geben. Daher sollte die Art der itoffensichtlich sein. Wenn der Typ xnicht offensichtlich ist, ist die Methode zu lang oder die Klasse zu groß.

Kevin Cline
quelle
7
Ist auch xein sehr falscher Variablenname für einen Container. In einigen Situationen können Sie sich wahrscheinlich nur den (semantisch wertvollen) Namen ansehen und die möglichen Operationen ableiten.
Max
@Max: Nur xals generisches Beispiel verwendet, neige ich dazu, ziemlich beschreibende Variablennamen zu verwenden.
Benutzer
@User Natürlich habe ich nicht angenommen, dass dies ein Beispiel aus der Praxis ist;)
Max
14

Einspruch ! Geladene Frage.

Können Sie mir erklären, warum der dritte Code enthalten ist ??, der erste und der zweite jedoch nicht? Der Fairness halber, Ihr Code muss lesen folgt als:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Dort. Das gleiche Problem, obwohl Sie es nicht benutzt haben auto.

In allen Fällen ist die Antwort dieselbe: Der Kontext ist wichtig . Sie können nicht sinnvoll isoliert über ein Stück Code sprechen. Selbst wenn Sie keine Vorlagen, sondern einen konkreten Typ verwendet hätten, hätte dies das Problem nur an eine andere Stelle verschoben, da der Leser Ihres Codes über die Deklaration dieses Typs Bescheid wissen müsste.

Bei Verwendung von auto in diesen Situationen Ihren Code unleserlich macht, sollten Sie dies als Warnzeichen dafür behandeln, dass etwas mit Ihrem Code-Design nicht stimmt. Natürlich gibt es Fälle, in denen Details auf niedriger Ebene von Bedeutung sind (z. B. bei Bitoperationen oder älteren APIs). In diesen Fällen kann ein expliziter Typ die Lesbarkeit verbessern. Aber im Allgemeinen - nein.

In Bezug auf var(da Sie es ausdrücklich erwähnt haben), gibt es auch einen großen Konsens in der C # -Community für die Verwendung var. Argumente gegen seine Verwendung beruhen im Allgemeinen auf Irrtümern .

Konrad Rudolph
quelle
1
Ich denke, der Punkt war, mit Auto wissen Sie nicht, was Sie als nächstes setzen ... Ist es Ihr Code-spezifisches "Etwas" oder ist es ein Datentyp-bezogenes Entpacken, um zu Ihrem Datenobjekt zu gelangen, das die Methode "Etwas" hat
Michael Shaw
1
@Ptolemy Und mein Punkt ist: In den anderen beiden Codes weißt du auch nicht (allgemein), was als nächstes zu setzen ist: Tist für den Benutzer so undurchsichtig wie auto. Trotzdem soll der eine in Ordnung sein und der andere nicht ?! Das ergibt keinen Sinn. Im Falle von OP Thandelt es sich um eine Vertretung für einen beliebigen Typ. In echtem Code kann es sich um die Verwendung von Vorlagen (for typename std::vector<T>::iterator…)oder einer Klassenschnittstelle handeln. In beiden Fällen ist der tatsächliche Typ vor dem Benutzer verborgen, und dennoch schreiben wir diesen Code routinemäßig ohne Probleme.
Konrad Rudolph
1
Eigentlich ja. Wenn es sich um einen Vektor handelt, wissen Sie, dass Sie dies tun müssen -> und haben dann Zugriff auf Ihren Datentyp. Wenn es sich um eine Karte handelt, wissen Sie, dass Sie Folgendes tun müssen -> second-> und haben dann Zugriff auf Ihren Datentyp. Wenn es sich um eine automatische Zuordnung handelt, wissen Sie nicht, was Sie tun müssen, um Zugriff auf Ihren Datentyp zu erhalten. Sie scheinen den "Was ist der in der STL-Auflistung enthaltene Datentyp" mit dem "Welchen STL-Auflistungstyp haben wir" zu verwechseln. Auto macht dieses Problem noch schlimmer.
Michael Shaw
1
@Ptolemy Alle diese Argumente sind bei der Verwendung genauso wahr auto. Es ist trivial zu sehen, welche Operationen xvom Kontext unterstützt werden. Tatsächlich gibt Ihnen der Typ keine zusätzlichen Informationen: In beiden Fällen benötigen Sie eine sekundäre (IDE, Dokumentation, Kenntnisse / Speicher), um die unterstützten Vorgänge anzuzeigen.
Konrad Rudolph
1
@Ptolemy Das ist nur der Fall , wenn Sie in der stark gewundenen Situation sind , dass Sie nicht was wissen beginkehrt aber Sie tun wissen , was std::vector<>::iteratorist. Und Sie müssen ein schlechtes Programmiertool verwenden, das Ihnen diese Informationen nicht trivial liefern kann. Das ist sehr verworren. In der Realität kennen Sie entweder beide beginund / iteratoroder keine und sollten eine IDE oder einen Editor verwenden, mit dem Sie die relevanten Informationen problemlos zur Verfügung stellen können. Das kann jeder moderne IDE- und Programmiereditor.
Konrad Rudolph
11

PROFI

Dein Code :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

wird wegen des vorlagenabhängigen Namens nicht kompiliert.

Dies ist die korrekte Syntax:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Nun schauen Sie, wie lang die Typdeklaration ist. Hier erfahren Sie, warum das autoSchlüsselwort eingeführt wurde. Dies :

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

ist prägnanter. Das ist also ein Profi.


CON

Du musst ein bisschen vorsichtig sein. Mit dem Schlüsselwort erhalten autoSie den Typ, den Sie deklariert haben.

Beispielsweise :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

vs

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

Zum Schluss: Ja, Sie sollten es, aber nicht überbeanspruchen. Einige Leute neigen dazu, es zu oft zu benutzen und setzen auto überall ein, wie im nächsten Beispiel:

auto i = 0;

vs

int i = 0;
BЈовић
quelle
auto i = 0. Schuldig. Ich mach das. Aber das liegt daran, dass ich weiß, dass 0das ein typisches Wort ist int. (und eine oktale Konstante ;-))
Laurent LA RIZZA
6

Ja du solltest! autolöscht den Typ nicht; Auch wenn Sie nicht wissen, was x.begin()ist, weiß der Compiler Bescheid und meldet einen Fehler, wenn Sie versuchen, den Typ falsch zu verwenden. Es ist auch nicht ungewöhnlich, mapmit a zu emulieren vector<pair<Key,Value>>, sodass der verwendete Code autofür beide Wörterbuchdarstellungen funktioniert.

zvrba
quelle
4

Ja, Sie sollten autoals Standardregel verwenden. Es hat Vorteile gegenüber der expliziten Angabe des Typs:

  • Sie müssen keine Dinge eingeben, die dem Compiler bereits bekannt sind.
  • Der Variablentyp "folgt" jeder Änderung der Rückgabewerttypen.
  • Auf diese Weise wird die unbeaufsichtigte Einführung impliziter Konvertierungen und das Slicing bei der Initialisierung lokaler Variablen verhindert.
  • Einige explizite Typberechnungen in Vorlagen sind nicht mehr erforderlich.
  • Die Benennung von Rückgabetypen mit langen Namen entfällt. (diejenigen, die Sie aus der Compiler-Diagnose kopieren und einfügen)

Hier haben Sie die Wahl. Es gibt auch Fälle, in denen Sie keine Wahl haben:

  • Es erlaubt die Deklaration von Variablen von nicht sprechbaren Typen, wie dem Typ eines Lambdas.

Vorausgesetzt, Sie wissen genau, was Sie autotun, hat es keine Nachteile.

Laurent LA RIZZA
quelle