Order of evaluation
Die Reihenfolge der Auswertung jedes Teils eines Ausdrucks, einschließlich der Reihenfolge der Auswertung von Funktionsargumenten, ist unspezifiziert (mit einigen unten aufgeführten Ausnahmen). Der Compiler kann Operanden und andere Teilausdrücke in beliebiger Reihenfolge auswerten und kann eine andere Reihenfolge wählen, wenn derselbe Ausdruck erneut ausgewertet wird.
Es gibt kein Konzept von Links-nach-rechts oder Rechts-nach-links Auswertung in C++. Dies ist nicht zu verwechseln mit Links-nach-rechts und Rechts-nach-links Assoziativität von Operatoren: der Ausdruck a ( ) + b ( ) + c ( ) wird geparst als ( a ( ) + b ( ) ) + c ( ) aufgrund der Links-nach-rechts Assoziativität von operator + , aber c ( ) könnte zuerst, zuletzt oder zwischen a ( ) oder b ( ) zur Laufzeit ausgewertet werden:
#include <cstdio> int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); } void z(int, int, int) {} int main() { z(a(), b(), c()); // alle 6 Permutationen der Ausgabe sind erlaubt return a() + b() + c(); // alle 6 Permutationen der Ausgabe sind erlaubt }
Mögliche Ausgabe:
b c a c a b
Inhaltsverzeichnis |
"Sequenced before"-Regeln (seit C++11)
Auswertung von Ausdrücken
Die Auswertung jedes Ausdrucks umfasst:
- Wertberechnungen : Berechnung des Werts, der durch den Ausdruck zurückgegeben wird. Dies kann die Bestimmung der Identität des Objekts (Glvalue-Auswertung, z.B. wenn der Ausdruck eine Referenz auf ein Objekt zurückgibt) oder das Lesen eines zuvor einem Objekt zugewiesenen Werts (Prvalue-Auswertung, z.B. wenn der Ausdruck eine Zahl oder einen anderen Wert zurückgibt) umfassen.
- Einleitung von Nebeneffekten : Zugriff (Lesen oder Schreiben) auf ein durch einen volatile-Glvalue bezeichnetes Objekt, Modifikation (Schreiben) eines Objekts, Aufruf einer Bibliotheks-E/A-Funktion oder Aufruf einer Funktion, die eine dieser Operationen durchführt.
Reihenfolge
Sequenced before
ist eine asymmetrische, transitive, paarweise Beziehung zwischen Auswertungen
A
und
B
innerhalb desselben Threads.
-
Wenn
AvorBsequenziert ist (oder äquivalent,BnachAsequenziert ist), dann wird die Auswertung vonAabgeschlossen sein, bevor die Auswertung vonBbeginnt. -
Wenn
Anicht vorBsequenziert ist undBvorAsequenziert ist, dann wird die Auswertung vonBabgeschlossen sein, bevor die Auswertung vonAbeginnt. -
Wenn
Anicht vorBsequenziert ist undBnicht vorAsequenziert ist, dann existieren zwei Möglichkeiten:-
Die Auswertungen von
AundBsind nicht sequenziert : sie können in beliebiger Reihenfolge ausgeführt werden und können sich überlappen (innerhalb eines einzelnen Ausführungsthreads kann der Compiler die CPU-Befehle, dieAundBumfassen, verschachteln). -
Die Auswertungen von
Aund B sind indeterminiert sequenziert : sie können in beliebiger Reihenfolge ausgeführt werden, dürfen sich aber nicht überlappen: entweder wirdAvorBabgeschlossen, oderBwird vorAabgeschlossen. Die Reihenfolge kann beim nächsten Auswerten des gleichen Ausdrucks umgekehrt sein.
-
Die Auswertungen von
Ein Ausdruck X wird als vorangestellt zu einem Ausdruck Y bezeichnet, wenn jede Wertberechnung und jeder Nebeneffekt von X vor jeder Wertberechnung und jedem Nebeneffekt des Ausdrucks Y ausgeführt wird.
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) |
| Die Regel 10 hat eine Ausnahme: Funktionsaufrufe, die von einem Standardbibliotheksalgorithmus unter der std::execution::par_unseq Ausführungsrichtlinie ausgeführt werden, sind nicht sequenziert und können beliebig miteinander verschachtelt werden. | (since C++17) |
|
13)
In einem Funktionsaufrufausdruck wird der Ausdruck, der die Funktion benennt, vor jedem Argumentausdruck und jedem Standardargument sequenziert.
14)
In einem Funktionsaufruf sind Wertberechnungen und Seiteneffekte der Initialisierung jedes Parameters in unbestimmter Reihenfolge bezüglich Wertberechnungen und Seiteneffekten jedes anderen Parameters.
15)
Jeder überladene Operator befolgt die Sequenzierungsregeln des eingebauten Operators, den er überlädt, wenn er mit Operator-Notation aufgerufen wird.
16)
In einem Indexausdruck
E1
[
E2
]
wird
E1
vor
E2
sequenziert.
17)
In einem Zeiger-auf-Mitglied-Ausdruck
E1.
*
E2
oder
E1
-
>
*
E2
wird
E1
vor
E2
sequenziert (sofern der dynamische Typ von
E1
nicht das Mitglied enthält, auf das
E2
verweist).
18)
In einem Schiebeoperatorausdruck
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 durch Kommas getrennten Liste von Ausdrücken in einem geklammerten Initialisierer wird wie für einen Funktionsaufruf ausgewertet (in unbestimmter Reihenfolge).
|
(seit C++17) |
Undefiniertes Verhalten
Das Verhalten ist undefined in den folgenden Fällen:
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
- einen Nebeneffekt auf derselben Speicherstelle
- eine Werteberechnung unter Verwendung des Werts eines beliebigen Objekts in derselben Speicherstelle
- das Beginnen oder Beenden der Lebensdauer eines Objekts, das Speicher belegt, der sich mit der Speicherstelle überschneidet
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
Sequenzpunkt-Regeln (bis C++11)
Pre-C++11 Definitionen
Die Auswertung eines Ausdrucks kann Nebeneffekte erzeugen, welche sind: Zugriff auf ein Objekt, das durch einen volatile L-Wert bezeichnet wird, Modifizieren eines Objekts, Aufrufen einer Bibliotheks-I/O-Funktion oder Aufrufen einer Funktion, die eine dieser Operationen durchführt.
Ein sequence point ist ein Punkt in der Ausführungssequenz, an dem alle Nebeneffekte der vorherigen Auswertungen in der Sequenz abgeschlossen sind und keine Nebeneffekte der nachfolgenden Auswertungen begonnen haben.
Pre-C++11 Regeln
a && b a || b a ? b : c a , b
Pre-C++11 Undefiniertes Verhalten
Das Verhalten ist undefined in den folgenden Fällen:
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
Fehlerberichte
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 |
Reihenfolge der Zerstörung automatischer
Variablen bei Funktionsrückgabe war nicht explizit |
Reihenfolgeregeln hinzugefügt |
| CWG 1949 | C++11 | "sequenced after" wurde verwendet, aber nicht im C++-Standard definiert |
definiert als das Inverse
von "sequenced before" |
| CWG 1953 | C++11 |
Seiteneffekte und Wertberechnungen bezüglich eines Speicher-
orts konnten in Bezug auf Beginn oder Ende der Lebensdauer eines Objekts am selben Speicherort ungeordnet sein |
das Verhalten ist
in diesem Fall undefiniert |
| CWG 2146 | C++98 | die Fälle mit undefiniertem Verhalten berücksichtigten keine Bitfelder | berücksichtigt |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 6.9.1 Programmausführung [intro.execution]
-
- 7.6.1.6 Inkrement und Dekrement [expr.post.incr]
-
- 7.6.2.8 New [expr.new]
-
- 7.6.14 Logischer UND-Operator [expr.log.and]
-
- 7.6.15 Logischer ODER-Operator [expr.log.or]
-
- 7.6.16 Bedingungsoperator [expr.cond]
-
- 7.6.19 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren [expr.ass]
-
- 7.6.20 Kommaoperator [expr.comma]
-
- 9.4.5 Listeninitialisierung [dcl.init.list]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 6.9.1 Programmausführung [intro.execution]
-
- 7.6.1.5 Inkrement und Dekrement [expr.post.incr]
-
- 7.6.2.7 New [expr.new]
-
- 7.6.14 Logischer UND-Operator [expr.log.and]
-
- 7.6.15 Logischer ODER-Operator [expr.log.or]
-
- 7.6.16 Bedingungsoperator [expr.cond]
-
- 7.6.19 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren [expr.ass]
-
- 7.6.20 Kommaoperator [expr.comma]
-
- 9.4.4 Listeninitialisierung [dcl.init.list]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 4.6 Programmausführung [intro.execution]
-
- 8.2.6 Inkrement und Dekrement [expr.post.incr]
-
- 8.3.4 New [expr.new]
-
- 8.14 Logischer UND-Operator [expr.log.and]
-
- 8.15 Logischer ODER-Operator [expr.log.or]
-
- 8.16 Bedingungsoperator [expr.cond]
-
- 8.18 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren [expr.ass]
-
- 8.19 Kommaoperator [expr.comma]
-
- 11.6.4 Listeninitialisierung [dcl.init.list]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 1.9 Programmausführung [intro.execution]
-
- 5.2.6 Inkrement und Dekrement [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Logischer UND-Operator [expr.log.and]
-
- 5.15 Logischer ODER-Operator [expr.log.or]
-
- 5.16 Bedingungsoperator [expr.cond]
-
- 5.17 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren [expr.ass]
-
- 5.18 Kommaoperator [expr.comma]
-
- 8.5.4 Listeninitialisierung [dcl.init.list]
- C++11-Standard (ISO/IEC 14882:2011):
-
- 1.9 Programmausführung [intro.execution]
-
- 5.2.6 Inkrement und Dekrement [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Logischer UND-Operator [expr.log.and]
-
- 5.15 Logischer ODER-Operator [expr.log.or]
-
- 5.16 Bedingungsoperator [expr.cond]
-
- 5.17 Zuweisungs- und zusammengesetzte Zuweisungsoperatoren [expr.ass]
-
- 5.18 Komma-Operator [expr.comma]
-
- 8.5.4 Listeninitialisierung [dcl.init.list]
Siehe auch
- Operator-Präzedenz die definiert, wie Ausdrücke aus ihrer Quellcode-Darstellung aufgebaut werden.
|
C-Dokumentation
für
Auswertungsreihenfolge
|