Menu

C++ Range-based for-Schleife: Syntax, auto und Referenzen

Die C++ range-based for-Schleife erklärt: sauberes Iterieren über Arrays, Vektoren, Strings und Maps, warum du auto& und const auto& verwenden solltest und welche Kopier- und Iterator-Invalidierungs-Fallen du vermeiden musst.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

„Für jedes Element, mach das"

Die klassische Zähl-for-Schleife ist großartig, wenn du einen Index zum Steuern hast. Aber meistens interessiert dich der Index gar nicht - du willst einfach jedes Element eines Containers anfassen. for (int i = 0; i < v.size(); i++) dafür zu schreiben, ist umständlich, und i ist nur einen Off-by-one-Fehler davon entfernt, außerhalb der Grenzen zu lesen.

C++11 hat genau dafür die range-based for-Schleife eingeführt. Du benennst eine Variable, zeigst auf einen Container, und die Schleife läuft jedes Element für dich ab:

Kein Index, kein .size(), keine Grenzen, die man falsch machen kann. Lies es als „für jedes s in scores". Es funktioniert mit rohen Arrays, std::vector, std::string, std::map und allem anderen, das begin() und end() bereitstellt.

Lass auto den Typ wählen

Den Elementtyp auszuschreiben funktioniert, ist aber fragil - ändere den Typ des Containers, und jede Schleife muss sich ebenfalls ändern. Kombiniere die range-based for mit auto, und der Compiler leitet den Elementtyp für dich ab:

Es gibt hier allerdings versteckte Kosten. Einfaches auto name leitet string ab und kopiert jedes Element bei jedem Durchlauf in name. Bei einem int ist das gratis; bei einem string oder einem großen Struct ist es eine verschwendete Allokation pro Iteration. Die Lösung sind Referenzen, und das ist das Nächste, was es zu verstehen gilt.

An Ort und Stelle ändern mit auto&

Wenn du auto x schreibst, bekommst du eine Kopie - eine Zuweisung an x ändert also die Kopie, nicht den Container. Achte auf diese Falle:

Die Verzehnfachung tut still nichts, weil n eine Wegwerf-Kopie ist. Um die Elemente wirklich zu bearbeiten, nimm sie per Referenz mit auto&:

Das einzelne & ist der ganze Unterschied zwischen „nur anschauen, nicht anfassen" und „an Ort und Stelle bearbeiten". Wenn du dich jemals fragst, warum deine Änderungen verschwinden, ist fast immer dies der Grund.

Lesen ohne Kopieren: const auto&

Wenn du Elemente nur lesen musst, sie aber teuer zu kopieren sind, verwende const auto&. Die Referenz vermeidet die Kopie, und const dokumentiert (und erzwingt), dass du nichts veränderst:

Eine gute Faustregel:

for (auto x : c)         // Kopie     - günstige Typen (int, char, Zeiger)
for (auto& x : c)        // bearbeiten - du willst die Elemente ändern
for (const auto& x : c)  // lesen      - schwere Typen, die du nur betrachtest

Verwende standardmäßig const auto& beim Lesen und auto& beim Schreiben. Greife nur bei wirklich kleinen, günstig zu kopierenden Typen zum einfachen auto.

Über Maps und Pairs iterieren

Eine range-based for über eine std::map liefert dir für jeden Eintrag ein std::pair mit .first (dem Schlüssel) und .second (dem Wert). Seit C++17 erlauben Structured Bindings, dieses Pair direkt im Schleifenkopf in zwei benannte Variablen zu entpacken:

[name, age] ist weitaus klarer, als überall entry.first und entry.second zu wiederholen. Behalte auch hier das const auto& bei - der Schlüssel eines map-Eintrags ist ein string, daher wäre das Kopieren jedes Pairs Verschwendung.

Die Falle: Während der Schleife nicht die Größe ändern

Die größte Falle ist, die Größe des Containers zu ändern, während eine range-based for ihn durchläuft. Der Aufruf von push_back, erase, insert oder clear kann den zugrunde liegenden Speicher neu allozieren und die internen Iteratoren der Schleife invalidieren - das Ergebnis ist undefiniertes Verhalten, also Abstürze oder Datenmüll, kein freundlicher Fehler:

vector<int> v = {1, 2, 3};
for (int x : v) {
    v.push_back(x);   // UNDEFINIERTES VERHALTEN - Neuallokation invalidiert den Bereich
}

Wenn du während der Verarbeitung Elemente hinzufügen oder entfernen musst, wechsle zu einer index- oder iteratorbasierten for-Schleife und verwalte die Grenzen selbst, oder baue einen separaten Ergebniscontainer und tausche ihn anschließend ein. Zwei kleinere Fallen aus derselben Familie: Binde eine range-based for niemals an ein Temporäres, das sofort stirbt (for (auto x : makeVector()) ist in Ordnung, aber for (auto& x : someObj.getTempVector()) kann baumeln), und denk daran, dass for (auto& c : myString) dir erlaubt, einzelne Zeichen an Ort und Stelle zu verändern.

Als Nächstes: Funktionen

Die range-based for-Schleife räumt das Iterieren auf, und die auto / auto& / const auto&-Entscheidungen, die du gerade gelernt hast, übertragen sich direkt auf eines der wichtigsten Werkzeuge in C++. Als Nächstes verpacken wir Logik in wiederverwendbare Funktionen - wir geben Code einen Namen, Parameter und einen Rückgabewert, damit du ihn von überall aufrufen kannst, statt dich zu wiederholen.

Häufig gestellte Fragen

Was ist eine range-based for-Schleife in C++?

Eine range-based for-Schleife besucht jedes Element eines Containers (Array, vector, string, map usw.), ohne dass du einen Index oder Iterator verwalten musst. Die Syntax lautet for (auto x : container) { ... }. Sie wurde mit C++11 eingeführt und ist die sauberste Art zu sagen "mach das für jedes Element".

Wann sollte ich in einer range-based for-Schleife auto& statt auto verwenden?

Verwende auto& x, wenn du die Elemente an Ort und Stelle verändern willst, und const auto& x, wenn du sie nur liest, aber das Kopieren vermeiden möchtest (wichtig für string, vector oder große Objekte). Einfaches auto x erstellt bei jeder Iteration eine Kopie - bei günstigen Typen wie int in Ordnung, sonst Verschwendung.

Kann man in C++ die Größe eines Vektors innerhalb einer range-based for-Schleife ändern?

Nein. Der Aufruf von push_back, erase, insert oder clear auf dem Container, den du gerade durchläufst, invalidiert die internen Iteratoren der Schleife und ist undefiniertes Verhalten - es kann abstürzen oder die Daten still beschädigen. Wenn du während des Durchlaufs Elemente hinzufügen oder entfernen musst, verwende stattdessen eine index- oder iteratorbasierte for-Schleife.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S