Reihenfolge der Auswertung
Die Reihenfolge der Auswertung jeglicher Teile eines beliebigen Ausdrucks, einschließlich der Reihenfolge der Auswertung von Funktionsargumenten, ist nicht spezifiziert (mit einigen Ausnahmen, die unten aufgeführt sind). Der Compiler kann Operanden und andere Unterausdrücke in beliebiger Reihenfolge auswerten und kann eine andere Reihenfolge wählen, wenn derselbe Ausdruck erneut ausgewertet wird.
In C++ gibt es kein Konzept der Links-nach-rechts- oder Rechts-nach-links-Auswertung. Dies ist nicht mit der Links-nach-rechts- und Rechts-nach-links-Assoziativität von Operatoren zu verwechseln: Der Ausdruck a() + b() + c() wird aufgrund der Links-nach-rechts-Assoziativität von operator+ als (a() + b()) + c() geparst, aber c() kann zur Laufzeit zuerst, zuletzt oder zwischen a() oder b() ausgewertet werden.
Mögliche Ausgabe
b c a c a b
Inhalt |
[bearbeiten] "Sequenced before"-Regeln (seit C++11)
[bearbeiten] Auswertung von Ausdrücken
Die Auswertung jedes Ausdrucks beinhaltet
- Wertberechnungen: Berechnung des Werts, der vom Ausdruck zurückgegeben wird. Dies kann die Ermittlung der Identität des Objekts (glvalue-Auswertung, z. B. wenn der Ausdruck eine Referenz auf ein Objekt zurückgibt) oder das Lesen des zuvor einem Objekt zugewiesenen Werts beinhalten (prvalue-Auswertung, z. B. wenn der Ausdruck eine Zahl oder einen anderen Wert zurückgibt).
- Einleitung von Seiteneffekten: Zugriff (Lesen oder Schreiben) auf ein Objekt, das durch einen volatile-lvalue bezeichnet wird, Modifikation (Schreiben) eines Objekts, Aufruf einer Bibliotheks-I/O-Funktion oder Aufruf einer Funktion, die eine dieser Operationen ausführt.
[bearbeiten] Reihenfolge
Sequenced before ist eine asymmetrische, transitive, paarweise Beziehung zwischen Auswertungen A und B innerhalb desselben Threads.
- Wenn
AvorBausgewertet wird (oder, gleichbedeutend,BnachAausgewertet wird), dann ist die Auswertung vonAabgeschlossen, bevor die Auswertung vonBbeginnt. - Wenn
Anicht vorBausgewertet wird undBvorAausgewertet wird, dann ist die Auswertung vonBabgeschlossen, bevor die Auswertung vonAbeginnt. - Wenn
Anicht vorBausgewertet wird undBnicht vorAausgewertet wird, dann existieren zwei Möglichkeiten:- Auswertungen von
AundBsind unsequenced : Sie können in beliebiger Reihenfolge durchgeführt werden und sich überlappen (innerhalb eines einzelnen Ausführungs-Threads kann der Compiler die CPU-Instruktionen, aus denenAundBbestehen, verschachteln). - Auswertungen von
AundBsind indeterminately sequenced : Sie können in beliebiger Reihenfolge durchgeführt werden, dürfen sich aber nicht überlappen: Entweder istAvorBabgeschlossen, oderBist vorAabgeschlossen. Die Reihenfolge kann beim nächsten Mal, wenn derselbe Ausdruck ausgewertet wird, umgekehrt sein.
- Auswertungen von
Ein Ausdruck X wird als sequenced before (sequenziert vor) einem Ausdruck Y bezeichnet, wenn jede Wertberechnung und jeder Seiteneffekt, der mit X verbunden ist, vor jeder Wertberechnung und jedem Seiteneffekt, der mit dem Ausdruck Y verbunden ist, sequenziert wird.
[bearbeiten] Regeln
- jeder Argumentausdruck und der Postfix-Ausdruck, der func bezeichnet
|
(seit C++26) |
- jeder Ausdruck oder jede Anweisung im Körper von func
|
(seit C++26) |
| Regel 10 hat eine Ausnahme: Funktionsaufrufe, die von einem Standardbibliotheksalgorithmus unter Verwendung der Ausführungsrichtlinie std::execution::par_unseq ausgeführt werden, sind unsequenziert und können beliebig miteinander verschachtelt werden. | (seit C++17) |
|
13) In einem Funktionsaufrufausdruck wird der Ausdruck, der die Funktion benennt, vor jedem Argumentausdruck und jedem Standardargument sequenziert.
14) Bei einem Funktionsaufruf sind die Wertberechnungen und Seiteneffekte der Initialisierung jedes Parameters indeterminately sequenced mit den Wertberechnungen und Seiteneffekten jedes anderen Parameters.
15) Jeder überladene Operator befolgt die Sequenzierungsregeln des integrierten Operators, den er bei der Verwendung von Operatornotation überlädt.
16) In einem Subskriptausdruck E1[E2] wird E1 vor E2 sequenziert.
17) In einem Zeiger-auf-Mitglied-Ausdruck E1.*E2 oder E1->*E2 wird E1 vor E2 sequenziert (es sei denn, der dynamische Typ von E1 enthält nicht das Mitglied, auf das sich E2 bezieht).
18) In einem Shift-Operator-Ausdruck E1 << E2 und E1 >> E2 wird E1 vor E2 sequenziert.
19) In jedem einfachen Zuweisungsausdruck E1 = E2 und jedem zusammengesetzten Zuweisungsausdruck E1 @= E2 wird E2 vor E1 sequenziert.
20) Jeder Ausdruck in einer kommaseparierten Liste von Ausdrücken in einem parenthetischen Initialisierer wird so ausgewertet, als wäre es ein Funktionsaufruf (indeterminately-sequenced). |
(seit C++17) |
[bearbeiten] Undefined behavior (nicht definiertes Verhalten)
Das Verhalten ist in den folgenden Fällen nicht definiert
i = ++i + 2; // well-defined i = i++ + 2; // undefined behavior until C++17 f(i = -2, i = -2); // undefined behavior until C++17 f(++i, ++i); // undefined behavior until C++17, unspecified after C++17 i = ++i + i++; // undefined behavior
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
- ein Seiteneffekt am selben Speicherort
- eine Wertberechnung, die den Wert eines Objekts an demselben Speicherort verwendet
- das Starten oder Beenden der Lebensdauer eines Objekts, das Speicherplatz belegt, der mit dem Speicherort überlappt.
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
[bearbeiten] Sequenzpunkt-Regeln (bis C++11)
[bearbeiten] Definitionen vor C++11
Die Auswertung eines Ausdrucks kann Seiteneffekte hervorrufen, die sind: Zugriff auf ein Objekt, das durch einen volatile-lvalue bezeichnet wird, Modifikation eines Objekts, Aufruf einer Bibliotheks-I/O-Funktion oder Aufruf einer Funktion, die eine dieser Operationen durchführt.
Ein Sequenzpunkt ist ein Punkt in der Ausführungssequenz, an dem alle Seiteneffekte der vorherigen Auswertungen in der Sequenz abgeschlossen sind und keine Seiteneffekte der nachfolgenden Auswertungen begonnen haben.
[bearbeiten] Regeln vor C++11
a && b a || b a ? b : c a , b
[bearbeiten] Nicht definiertes Verhalten vor C++11
Das Verhalten ist in den folgenden Fällen nicht definiert
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior i = ++i + 1; // undefined behavior ++ ++i; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
cout << i << i++; // undefined behavior a[i] = i++; // undefined behavior
[bearbeiten] Defect reports
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 1885 | C++11 | Sequenzierung der Zerstörung von automatischen Variablen bei Funktionsrückgabe war nicht explizit |
Sequenzierungsregeln hinzugefügt |
| CWG 1949 | C++11 | "sequenced after" wurde verwendet, aber nicht im C++-Standard definiert. | definiert als die Umkehrung von "sequenced before" |
| CWG 1953 | C++11 | Seiteneffekte und Wertberechnungen, die einen Speicherort betreffen, konnten relativ zum Starten oder Beenden der Lebensdauer eines Objekts am selben Speicherort unsequenziert sein. |
Das Verhalten ist in diesem Fall nicht definiert. |
| CWG 2146 | C++98 | Die Fälle, die nicht definiertes Verhalten betrafen, berücksichtigten keine Bitfelder. | berücksichtigt. |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 6.9.1 Program execution [intro.execution]
- 7.6.1.6 Increment and decrement [expr.post.incr]
- 7.6.2.8 New [expr.new]
- 7.6.14 Logical AND operator [expr.log.and]
- 7.6.15 Logical OR operator [expr.log.or]
- 7.6.16 Conditional operator [expr.cond]
- 7.6.19 Assignment and compound assignment operators [expr.ass]
- 7.6.20 Comma operator [expr.comma]
- 9.4.5 List-initialization [dcl.init.list]
- C++20 Standard (ISO/IEC 14882:2020)
- 6.9.1 Program execution [intro.execution]
- 7.6.1.5 Increment and decrement [expr.post.incr]
- 7.6.2.7 New [expr.new]
- 7.6.14 Logical AND operator [expr.log.and]
- 7.6.15 Logical OR operator [expr.log.or]
- 7.6.16 Conditional operator [expr.cond]
- 7.6.19 Assignment and compound assignment operators [expr.ass]
- 7.6.20 Comma operator [expr.comma]
- 9.4.4 List-initialization [dcl.init.list]
- C++17 Standard (ISO/IEC 14882:2017)
- 4.6 Program execution [intro.execution]
- 8.2.6 Increment and decrement [expr.post.incr]
- 8.3.4 New [expr.new]
- 8.14 Logical AND operator [expr.log.and]
- 8.15 Logical OR operator [expr.log.or]
- 8.16 Conditional operator [expr.cond]
- 8.18 Assignment and compound assignment operators [expr.ass]
- 8.19 Comma operator [expr.comma]
- 11.6.4 List-initialization [dcl.init.list]
- C++14 Standard (ISO/IEC 14882:2014)
- 1.9 Program execution [intro.execution]
- 5.2.6 Increment and decrement [expr.post.incr]
- 5.3.4 New [expr.new]
- 5.14 Logical AND operator [expr.log.and]
- 5.15 Logical OR operator [expr.log.or]
- 5.16 Conditional operator [expr.cond]
- 5.17 Assignment and compound assignment operators [expr.ass]
- 5.18 Comma operator [expr.comma]
- 8.5.4 List-initialization [dcl.init.list]
- C++11 Standard (ISO/IEC 14882:2011)
- 1.9 Program execution [intro.execution]
- 5.2.6 Increment and decrement [expr.post.incr]
- 5.3.4 New [expr.new]
- 5.14 Logical AND operator [expr.log.and]
- 5.15 Logical OR operator [expr.log.or]
- 5.16 Conditional operator [expr.cond]
- 5.17 Assignment and compound assignment operators [expr.ass]
- 5.18 Comma operator [expr.comma]
- 8.5.4 List-initialization [dcl.init.list]
[bearbeiten] Siehe auch
- Operator-Priorität, die definiert, wie Ausdrücke aus ihrer Quellcode-Darstellung aufgebaut werden.
| C-Dokumentation für Reihenfolge der Auswertung
|