Namespaces
Variants

Range-based for loop (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
for
range- for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Führt eine for -Schleife über einen Bereich aus.

Wird als besser lesbares Äquivalent zur traditionellen for Schleife verwendet, die über einen Wertebereich operiert, wie beispielsweise alle Elemente in einem Container.

Inhaltsverzeichnis

Syntax

attr  (optional) for ( init-statement  (optional) item-declaration : range-initializer ) statement
attr - beliebige Anzahl von Attributen
init-statement - (seit C++20) eines der Folgenden
(seit C++23)

Beachten Sie, dass jede init-statement mit einem Semikolon enden muss. Deshalb wird sie informell oft als Ausdruck oder Deklaration gefolgt von einem Semikolon beschrieben.

item-declaration - eine Deklaration für jedes Range-Element
range-initializer - ein Ausdruck oder eine geschweifte Klammer-Initialisiererliste
statement - eine beliebige Anweisung (typischerweise eine zusammengesetzte Anweisung)

Erklärung

Die obige Syntax erzeugt Code, der dem Folgenden entspricht außer der Lebensdauerverlängerung von Temporaries des range-initializer (siehe unten ) (since C++23) (die Variablen und Ausdrücke in /* */ sind nur zur Veranschaulichung):

{

auto&& /* range */ = Bereichsinitialisierer  ;
for (auto /* begin */ = /* begin-expr */ , /* end */ = /* end-expr */ ;
/* begin */ != /* end */ ; ++ /* begin */ )
{
Elementdeklaration = * /* begin */ ;
Anweisung
}

}

(bis C++17)

{

auto&& /* range */ = Bereichsinitialisierer  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
Elementdeklaration = * /* begin */ ;
Anweisung
}

}

(seit C++17)
(bis C++20)

{

Initialisierungsanweisung
auto&& /* range */ = Bereichsinitialisierer  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
Elementdeklaration = * /* begin */ ;
Anweisung
}

}

(seit C++20)

range-initializer wird ausgewertet, um die Sequenz oder den Bereich für die Iteration zu initialisieren. Jedes Element der Sequenz wird wiederum dereferenziert und dazu verwendet, die Variable mit dem in item-declaration angegebenen Typ und Namen zu initialisieren.

item-declaration kann eines der folgenden sein:

Nur-Darstellungsausdrücke /* begin-expr */ und /* end-expr */ werden wie folgt definiert:

  • Wenn der Typ von /* range */ ein Verweis auf einen Array-Typ R ist:
  • Wenn R ein Array mit fester Größe N ist, /* begin-expr */ ist /* range */ und /* end-expr */ ist /* range */ + N .
  • Wenn R ein Array mit unbekannter Größe oder ein Array mit unvollständigem Typ ist, ist das Programm fehlerhaft.
  • Wenn der Typ von /* range */ eine Referenz auf einen Klassentyp C ist, und die Suche im Gültigkeitsbereich von C nach den Namen " begin " und " end " jeweils mindestens eine Deklaration findet, dann ist /* begin-expr */ gleich /* range */ . begin ( ) und /* end-expr */ ist gleich /* range */ . end ( ) .
  • Andernfalls ist /* begin-expr */ gleich begin ( /* range */ ) und /* end-expr */ ist gleich end ( /* range */ ) , wobei " begin " und " end " mittels argument-dependent lookup gefunden werden (non-ADL lookup wird nicht durchgeführt).

Wenn die Schleife innerhalb der statement beendet werden muss, kann eine break statement als abschließende Anweisung verwendet werden.

Wenn die aktuelle Iteration innerhalb der Anweisung beendet werden muss, kann eine continue Anweisung als Abkürzung verwendet werden.

Wenn ein Name, der in der init-statement eingeführt wird, im äußersten Block der statement erneut deklariert wird, ist das Programm fehlerhaft:

for (int i : {1, 2, 3})
    int i = 1; // Fehler: Neudeklaration

Temporärer Bereichsinitialisierer

Wenn der range-initializer ein temporäres Objekt zurückgibt, wird seine Lebensdauer bis zum Ende der Schleife verlängert, wie durch die Bindung an die Weiterleitungsreferenz /* range */ angezeigt.

Die Lebensdauern aller Temporaries innerhalb des range-initializer werden nicht verlängert es sei denn, sie würden sonst am Ende des range-initializer zerstört werden (since C++23) .

// wenn foo() als Wert zurückgibt
for (auto& x : foo().items()) { /* ... */ } // bis C++23 undefiniertes Verhalten

Dieses Problem kann umgangen werden durch Verwendung einer Init-Anweisung :

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK
(seit C++20)


Beachten Sie, dass selbst in C++23 Nicht-Referenzparameter von Zwischenfunktionsaufrufen keine Lebensdauerverlängerung erhalten (da sie in einigen ABIs im Aufgerufenen und nicht im Aufrufer zerstört werden), aber das ist nur ein Problem für Funktionen, die ohnehin fehlerhaft sind:

using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; } // always returns a dangling reference
T g();
void foo()
{
    for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
    for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
}
(seit C++23)

Hinweise

Wenn der range-initializer eine geschweifte Klammer-Initialisierungsliste ist, /* range */ wird als Referenz auf ein std::initializer_list abgeleitet.

Es ist sicher und in der Tat in generischem Code vorzuziehen, Deduktion für Forwarding-Referenzen zu verwenden, for ( auto && var : sequence ) .

Die Member-Interpretation wird verwendet, wenn der Bereichstyp einen Member namens " begin " und einen Member namens " end " besitzt. Dies erfolgt unabhängig davon, ob der Member ein Typ, ein Datenmember, eine Funktion oder ein Enumerator ist, und unabhängig von seiner Zugänglichkeit. Daher kann eine Klasse wie class meow { enum { begin = 1 , end = 2 } ; /* rest of class */ } ; nicht mit der bereichsbasierten for -Schleife verwendet werden, selbst wenn die namespace-weiten " begin "/" end "-Funktionen vorhanden sind.

Während die in der item-declaration deklarierte Variable normalerweise in der statement verwendet wird, ist dies nicht zwingend erforderlich.

Seit C++17 müssen die Typen von /* begin-expr */ und /* end-expr */ nicht mehr identisch sein, und tatsächlich muss der Typ von /* end-expr */ kein Iterator sein: Es muss lediglich eine Ungleichheitsvergleich mit einem Iterator möglich sein. Dies ermöglicht es, einen Bereich durch ein Prädikat zu begrenzen (z.B. "der Iterator zeigt auf ein Nullzeichen").

(since C++17)

Bei Verwendung mit einem (nicht-konstanten) Objekt, das Copy-on-Write-Semantik besitzt, kann die bereichsbasierte for Schleife ein tiefes Kopieren auslösen, indem sie (implizit) die nicht-konstante begin() Memberfunktion aufruft.

Falls dies unerwünscht ist (beispielsweise weil die Schleife das Objekt nicht tatsächlich verändert), kann dies durch Verwendung von std::as_const vermieden werden:

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;
// for (auto x : str) { /* ... */ } // may cause deep copy
for (auto x : std::as_const(str)) { /* ... */ }
(seit C++17)
Feature-Test-Makro Wert Std Feature
__cpp_range_based_for 200907L (C++11) Bereichsbasierte for Schleife
201603L (C++17) Bereichsbasierte for Schleife mit unterschiedlichen begin / end Typen
202211L (C++23) Lebensdauerverlängerung für alle temporären Objekte in Bereichsinitialisierer

Schlüsselwörter

for

Beispiel

#include <iostream>
#include <vector>
int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
    for (const int& i : v) // Zugriff per const-Referenz
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto i : v) // Zugriff per Wert, der Typ von i ist int
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto&& i : v) // Zugriff per Forwarding-Referenz, der Typ von i ist int&
        std::cout << i << ' ';
    std::cout << '\n';
    const auto& cv = v;
    for (auto&& i : cv) // Zugriff per Forwarding-Referenz, der Typ von i ist const int&
        std::cout << i << ' ';
    std::cout << '\n';
    for (int n : {0, 1, 2, 3, 4, 5}) // Der Initialisierer kann eine
                                     // geschweifte Initialisierungsliste sein
        std::cout << n << ' ';
    std::cout << '\n';
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // Der Initialisierer kann ein Array sein
        std::cout << n << ' ';
    std::cout << '\n';
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // Die Schleifenvariable muss nicht verwendet werden
    std::cout << '\n';
    for (auto n = v.size(); auto i : v) // Die Init-Anweisung (C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // Typdefinition als Init-Anweisung (C++20)
        std::cout << i << ' ';
    std::cout << '\n';
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // Alias-Deklaration als Init-Anweisung (C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

Ausgabe:

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
1 1 1 1 1 1 
5 5 5 5 5 5 
0 1 2 3 4 5 
0 1 2 3 4 5

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Veröffentlichtes Verhalten Korrigiertes Verhalten
CWG 1442 C++11 es war nicht spezifiziert, ob die Suche nach Nicht-Mitglied-
" begin " und " end " die übliche unqualifizierte Suche einschließt
keine übliche unqualifizierte Suche
CWG 2220 C++11 die in init-statement eingeführten Namen konnten erneut deklariert werden das Programm ist in diesem Fall fehlerhaft
CWG 2825 C++11 wenn range-initializer eine geschweifte Initialisierungsliste ist,
werden die Nicht-Mitglied-" begin " und " end " gesucht
sucht in diesem Fall Mitglied-" begin "
und " end "
P0962R1 C++11 Mitglied-Interpretation wurde verwendet, wenn entweder
Mitglied-" begin " oder " end " vorhanden ist
wird nur verwendet, wenn beide vorhanden sind

Siehe auch

wendet ein unäres Funktionsobjekt auf Elemente eines Bereichs an
(Funktions-Template)