C ++ 11 bereichsbasierte Schleife: Element nach Wert oder Verweis auf const abrufen

215

Beim Lesen einiger Beispiele für bereichsbasierte Schleifen schlagen sie zwei Hauptmethoden vor: 1 , 2 , 3 , 4

std::vector<MyClass> vec;

for (auto &x : vec)
{
  // x is a reference to an item of vec
  // We can change vec's items by changing x 
}

oder

for (auto x : vec)
{
  // Value of x is copied from an item of vec
  // We can not change vec's items by changing x
}

Gut.

Wenn wir keine vecElemente ändern müssen, empfehlen IMO, Beispiele, die zweite Version (nach Wert) zu verwenden. Warum sie nicht etwas vorschlagen, auf das constverwiesen wird (Zumindest habe ich keinen direkten Vorschlag gefunden):

for (auto const &x : vec) // <-- see const keyword
{
  // x is a reference to an const item of vec
  // We can not change vec's items by changing x 
}

Ist es nicht besser Vermeidet es nicht eine redundante Kopie in jeder Iteration, während es eine ist const?

masoud
quelle

Antworten:

390

Wenn Sie die Elemente nicht ändern und keine Kopien erstellen möchten , auto const &ist dies die richtige Wahl:

for (auto const &x : vec)

Wer Sie zur Verwendung vorschlägt, auto &ist falsch. Ignoriere sie.

Hier ist eine Zusammenfassung:

  • Wählen Sie aus, auto xwann Sie mit Kopien arbeiten möchten.
  • Wählen Sie aus, auto &xwann Sie mit Originalelementen arbeiten möchten, und ändern Sie diese möglicherweise.
  • Wählen Sie aus, auto const &xwann Sie mit Originalelementen arbeiten möchten und diese nicht ändern möchten.
Nawaz
quelle
21
Danke für die tolle Antwort. Ich denke, es sollte auch darauf hingewiesen werden, dass const auto &xdies Ihrer dritten Wahl entspricht.
smg
7
@ Mloskot: Es ist gleichwertig. (und was meinst du mit "aber es ist die gleiche Syntax" ? Die Syntax ist beobachtbar unterschiedlich.)
Nawaz
14
Fehlend: auto&&Wenn Sie keine unnötige Kopie erstellen möchten und es Ihnen egal ist, ob Sie sie ändern oder nicht, und Sie einfach arbeiten möchten.
Yakk - Adam Nevraumont
4
Gilt die Referenzunterscheidung für Kopien, wenn Sie nur mit grundlegenden Typen wie int / double arbeiten?
4
@racarate: Ich kann die Gesamtgeschwindigkeit nicht kommentieren , und ich denke, niemand kann dies, ohne es vorher zu profilieren. Ehrlich gesagt werde ich meine Wahl nicht auf die Geschwindigkeit stützen, sondern auf die Klarheit des Codes. Wenn ich Unveränderlichkeit will, würde ich constsicher verwenden. Ob es jedoch wäre auto const &oder auto const wenig Unterschied hat. Ich würde mich dafür entscheiden auto const &, konsequenter zu sein.
Nawaz
23

Wenn Sie ein std::vector<int>oder haben std::vector<double>, ist es in Ordnung, auto(mit Wertkopie) anstelle von zu verwenden const auto&, da das Kopieren eines intoder eines doublebillig ist:

for (auto x : vec)
    ....

Wenn Sie jedoch eine haben std::vector<MyClass>, bei der es MyClasssich um eine nicht triviale Kopiersemantik handelt (z. B. std::stringeine komplexe benutzerdefinierte Klasse usw.), würde ich die Verwendung empfehlen const auto&, um Deep-Copies zu vermeiden :

for (const auto & x : vec)
    ....
Mr.C64
quelle
3

Wenn keine Änderungen erforderlich vecsind, empfehlen Beispiele die Verwendung der ersten Version.

Dann machen sie einen falschen Vorschlag.

Warum schlagen sie nichts vor, worauf const verweist?

Weil sie einen falschen Vorschlag machen :-) Was Sie erwähnen, ist richtig. Wenn Sie nur ein Objekt beobachten möchten, müssen Sie keine Kopie erstellen und es muss kein constVerweis darauf vorhanden sein.

BEARBEITEN:

Ich sehe, dass die Referenzen, die Sie alle verknüpfen, Beispiele für das Iterieren über einen Wertebereich intoder einen anderen grundlegenden Datentyp liefern. In diesem Fall intist das Erstellen einer Kopie im Grunde genommen gleichbedeutend mit einer Beobachtung (wenn nicht sogar effizienter als diese) , da das Kopieren von nicht teuer ist const &.

Dies ist jedoch bei benutzerdefinierten Typen im Allgemeinen nicht der Fall. Das Kopieren von UDTs kann teuer sein. Wenn Sie keinen Grund haben, eine Kopie zu erstellen (z. B. das abgerufene Objekt zu ändern, ohne das Original zu ändern), ist es vorzuziehen, a zu verwenden const &.

Andy Prowl
quelle
1

Ich werde hier das Gegenteil sagen und sagen, dass es auto const &in einem Bereich, der auf einer Schleife basiert, keine Notwendigkeit gibt . Sagen Sie mir, wenn Sie die folgende Funktion für dumm halten (nicht in ihrem Zweck, sondern in der Art und Weise, wie sie geschrieben ist):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Hier hat der Autor einen konstanten Verweis erstellt, vder für alle Operationen verwendet werden soll, die v nicht ändern. Dies ist meiner Meinung nach albern, und das gleiche Argument kann für die Verwendung auto const &als Variable in einem Bereich verwendet werden, der auf einer Schleife basiert, anstatt nur auto &.

Benjamin Lindley
quelle
3
@BenjaminLindley: Also kann ich schließen, dass Sie auch const_iteratorin einer for-Schleife dagegen argumentieren würden ? Wie stellen Sie sicher, dass Sie die Originalelemente aus einem Container nicht ändern, wenn Sie darüber iterieren?
Nawaz
2
@BenjaminLindley: Warum sollte Ihr Code ohne nicht kompiliert werden const_iterator? Lassen Sie mich raten, der Container, über den Sie iterieren, ist const. Aber warum ist es so const? Irgendwo verwenden Sie, constum sicherzustellen, was?
Nawaz
8
Der Vorteil der Erstellung von Objekten constliegt in den Vorteilen der Verwendung von private/ protectedin Klassen. Es vermeidet weitere Fehler.
Masoud
2
@BenjaminLindley: Oh, das habe ich übersehen. Dann ist in diesem Fall auch die Implementierung albern. Aber wenn diese Funktion nicht geändert würde v, wäre die Implementierung IMO in Ordnung. Und wenn die Schleife niemals die Objekte ändert, durch die sie iteriert, erscheint es mir richtig, einen Verweis auf zu haben const. Ich verstehe deinen Standpunkt. Meins ist, dass die Schleife eine Codeeinheit darstellt, ähnlich wie die Funktion, und innerhalb dieser Einheit gibt es keine Anweisung, die den Wert ändern muss. Daher können Sie die gesamte Einheit wie constbei einer constMitgliedsfunktion als "kennzeichnen" .
Andy Prowl
1
Sie denken also, const &dass Range-Based dumm ist, weil Sie ein irrelevantes imaginäres Beispiel eines imaginären Programmierers geschrieben haben, der etwas Dummes mit Lebenslaufqualifikation gemacht hat? ... OK dann
underscore_d