Order of evaluation
L'ordre d'évaluation de toute partie d'une expression, y compris l'ordre d'évaluation des arguments de fonction, est non spécifié (à l'exception de certains cas énumérés ci-dessous). Le compilateur peut évaluer les opérandes et autres sous-expressions dans n'importe quel ordre, et peut choisir un autre ordre lorsque la même expression est évaluée à nouveau.
Il n'existe pas de concept d'évaluation de gauche à droite ou de droite à gauche en C++. Ceci ne doit pas être confondu avec l'associativité gauche-droite et droite-gauche des opérateurs : l'expression a ( ) + b ( ) + c ( ) est analysée comme ( a ( ) + b ( ) ) + c ( ) en raison de l'associativité gauche-droite de operator + , mais c ( ) peut être évalué en premier, en dernier, ou entre a ( ) ou b ( ) à l'exécution :
#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()); // les 6 permutations de sortie sont autorisées return a() + b() + c(); // les 6 permutations de sortie sont autorisées }
Sortie possible :
b c a c a b
Table des matières |
Règles "séquencé avant" (depuis C++11)
Évaluation des Expressions
L'évaluation de chaque expression comprend :
- Calculs de valeur : calcul de la valeur retournée par l'expression. Cela peut impliquer la détermination de l'identité de l'objet (évaluation glvalue, par exemple si l'expression retourne une référence à un objet) ou la lecture de la valeur précédemment assignée à un objet (évaluation prvalue, par exemple si l'expression retourne un nombre ou une autre valeur).
- Initiation des effets secondaires : accès (lecture ou écriture) à un objet désigné par une glvalue volatile, modification (écriture) d'un objet, appel d'une fonction d'E/S de la bibliothèque, ou appel d'une fonction effectuant l'une de ces opérations.
Ordonnancement
Sequenced before
est une relation asymétrique, transitive et binaire entre les évaluations
A
et
B
au sein du même thread.
-
Si
Aest séquencé avantB(ou, de manière équivalente,Best séquencé aprèsA), alors l'évaluation deAsera terminée avant que l'évaluation deBne commence. -
Si
An'est pas séquencé avantBetBest séquencé avantA, alors l'évaluation deBsera terminée avant que l'évaluation deAne commence. -
Si
An'est pas séquencé avantBetBn'est pas séquencé avantA, alors deux possibilités existent :-
Les évaluations de
AetBsont non séquencées : elles peuvent être effectuées dans n'importe quel ordre et peuvent se chevaucher (au sein d'un seul thread d'exécution, le compilateur peut entrelacer les instructions CPU qui composentAetB). -
Les évaluations de
Aet B sont séquencées de manière indéterminée : elles peuvent être effectuées dans n'importe quel ordre mais ne peuvent pas se chevaucher : soitAsera terminé avantB, soitBsera terminé avantA. L'ordre peut être opposé la prochaine fois que la même expression est évaluée.
-
Les évaluations de
Une expression X est dite séquencée avant une expression Y si chaque calcul de valeur et chaque effet secondaire associé à X est séquencé avant chaque calcul de valeur et chaque effet secondaire associé à l'expression Y .
Règles
- chaque expression d'argument et l'expression postfixée désignant func
|
(depuis C++26) |
- chaque expression ou instruction dans le corps de func
|
(depuis C++26) |
| La règle 10 a une exception : les appels de fonction effectués par un algorithme de la bibliothèque standard s'exécutant sous la politique d'exécution std::execution::par_unseq ne sont pas séquencés et peuvent être entrelacés arbitrairement les uns avec les autres. | (since C++17) |
|
13)
Dans une expression d'appel de fonction, l'expression qui nomme la fonction est séquencée avant chaque expression d'argument et chaque argument par défaut.
14)
Dans un appel de fonction, les calculs de valeur et les effets secondaires de l'initialisation de chaque paramètre sont séquencés de manière indéterminée par rapport aux calculs de valeur et effets secondaires de tout autre paramètre.
15)
Chaque opérateur surchargé respecte les règles de séquencement de l'opérateur intégré qu'il surcharge lorsqu'il est appelé en utilisant la notation d'opérateur.
16)
Dans une expression d'indice
E1
[
E2
]
,
E1
est séquencé avant
E2
.
17)
Dans une expression pointeur-vers-membre
E1.
*
E2
ou
E1
-
>
*
E2
,
E1
est séquencé avant
E2
(sauf si le type dynamique de
E1
ne contient pas le membre auquel
E2
se réfère).
18)
Dans une expression d'opérateur de décalage
E1
<<
E2
et
E1
>>
E2
,
E1
est séquencé avant
E2
.
19)
Dans chaque expression d'affectation simple
E1
=
E2
et chaque expression d'affectation composée
E1 @
=
E2
,
E2
est séquencé avant
E1
.
20)
Chaque expression dans une liste d'expressions séparées par des virgules dans un initialiseur entre parenthèses est évaluée comme pour un appel de fonction (séquencement indéterminé).
|
(depuis C++17) |
Comportement indéfini
Le comportement est indéfini dans les cas suivants :
i = ++i + 2; // bien défini i = i++ + 2; // comportement indéfini jusqu'à C++17 f(i = -2, i = -2); // comportement indéfini jusqu'à C++17 f(++i, ++i); // comportement indéfini jusqu'à C++17, non spécifié après C++17 i = ++i + i++; // comportement indéfini
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
- un effet de bord sur le même emplacement mémoire
- un calcul de valeur utilisant la valeur de tout objet dans le même emplacement mémoire
- le début ou la fin de la durée de vie d'un objet occupant un stockage qui chevauche l'emplacement mémoire
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
Règles des points de séquence (jusqu'à C++11)
Définitions pré-C++11
L'évaluation d'une expression peut produire des effets secondaires, qui sont : accéder à un objet désigné par une lvalue volatile, modifier un objet, appeler une fonction d'E/S de bibliothèque, ou appeler une fonction qui effectue l'une de ces opérations.
Un point de séquence est un point dans la séquence d'exécution où tous les effets secondaires des évaluations précédentes dans la séquence sont terminés, et aucun effet secondaire des évaluations suivantes n'a commencé.
Règles pré-C++11
a && b a || b a ? b : c a , b
Comportement indéfini avant C++11
Le comportement est indéfini dans les cas suivants :
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
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 1885 | C++11 |
l'ordonnancement de la destruction des variables
automatiques au retour de fonction n'était pas explicite |
règles d'ordonnancement ajoutées |
| CWG 1949 | C++11 | « séquencé après » était utilisé mais non défini dans le standard C++ |
défini comme l'inverse
de « séquencé avant » |
| CWG 1953 | C++11 |
les effets de bord et calculs de valeur impliquant un emplacement
mémoire pouvaient être non séquencés par rapport au début ou à la fin de la durée de vie d'un objet dans le même emplacement mémoire |
le comportement est
indéfini dans ce cas |
| CWG 2146 | C++98 | les cas impliquant des comportements indéfinis ne prenaient pas en compte les champs de bits | pris en compte |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 6.9.1 Exécution du programme [intro.execution]
-
- 7.6.1.6 Incrément et décrément [expr.post.incr]
-
- 7.6.2.8 New [expr.new]
-
- 7.6.14 Opérateur ET logique [expr.log.and]
-
- 7.6.15 Opérateur OU logique [expr.log.or]
-
- 7.6.16 Opérateur conditionnel [expr.cond]
-
- 7.6.19 Opérateurs d'affectation et d'affectation composée [expr.ass]
-
- 7.6.20 Opérateur virgule [expr.comma]
-
- 9.4.5 Initialisation de liste [dcl.init.list]
- Norme C++20 (ISO/CEI 14882:2020) :
-
- 6.9.1 Exécution du programme [intro.execution]
-
- 7.6.1.5 Incrémentation et décrémentation [expr.post.incr]
-
- 7.6.2.7 Opérateur new [expr.new]
-
- 7.6.14 Opérateur ET logique [expr.log.and]
-
- 7.6.15 Opérateur OU logique [expr.log.or]
-
- 7.6.16 Opérateur conditionnel [expr.cond]
-
- 7.6.19 Opérateurs d'affectation et d'affectation composée [expr.ass]
-
- 7.6.20 Opérateur virgule [expr.comma]
-
- 9.4.4 Initialisation par liste [dcl.init.list]
- Norme C++17 (ISO/IEC 14882:2017) :
-
- 4.6 Exécution du programme [intro.execution]
-
- 8.2.6 Incrémentation et décrémentation [expr.post.incr]
-
- 8.3.4 Opérateur new [expr.new]
-
- 8.14 Opérateur logique ET [expr.log.and]
-
- 8.15 Opérateur logique OU [expr.log.or]
-
- 8.16 Opérateur conditionnel [expr.cond]
-
- 8.18 Opérateurs d'affectation et d'affectation composée [expr.ass]
-
- 8.19 Opérateur virgule [expr.comma]
-
- 11.6.4 Initialisation par liste [dcl.init.list]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 1.9 Exécution du programme [intro.execution]
-
- 5.2.6 Incrément et décrément [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Opérateur ET logique [expr.log.and]
-
- 5.15 Opérateur OU logique [expr.log.or]
-
- 5.16 Opérateur conditionnel [expr.cond]
-
- 5.17 Opérateurs d'affectation et d'affectation composée [expr.ass]
-
- 5.18 Opérateur virgule [expr.comma]
-
- 8.5.4 Initialisation de liste [dcl.init.list]
- Norme C++11 (ISO/CEI 14882:2011) :
-
- 1.9 Exécution du programme [intro.execution]
-
- 5.2.6 Incrément et décrément [expr.post.incr]
-
- 5.3.4 New [expr.new]
-
- 5.14 Opérateur logique ET [expr.log.and]
-
- 5.15 Opérateur logique OU [expr.log.or]
-
- 5.16 Opérateur conditionnel [expr.cond]
-
- 5.17 Opérateurs d'affectation et d'affectation composée [expr.ass]
-
- 5.18 Opérateur virgule [expr.comma]
-
- 8.5.4 Initialisation de liste [dcl.init.list]
Voir aussi
- Priorité des opérateurs qui définit la manière dont les expressions sont construites à partir de leur représentation en code source.
|
Documentation C
pour
Ordre d'évaluation
|