30 votes

Dans quelles versions de la norme C++ le comportement de "(i+=10)+=10" est-il indéfini ?

En C++, les éléments suivants ont-ils un comportement indéfini ?

int i = 0;
(i+=10)+=10;

Il y a eu un débat à ce sujet dans les commentaires de la conférence. ma réponse a Quel est le résultat de += en C et C++ ? La subtilité ici est que la réponse par défaut semble être "oui", alors qu'il semble que la réponse correcte soit "cela dépend de la version de la norme C++".

Si cela dépend de la version de la norme, veuillez expliquer où elle est UB et où elle ne l'est pas.

33voto

bames53 Points 38303

tl;dr : Le site séquence des modifications et des lectures effectuées dans (i+=10)+=10 est bien défini en C++98 et C++11, cependant en C++98 cela n'est pas suffisant pour rendre le comportement défini.

En C++98, des modifications multiples d'un même objet sans qu'il y ait une intervention de la part de l'utilisateur sont possibles. point de séquence entraîne un comportement indéfini, même lorsque l'ordre de ces modifications est bien spécifié. Cette expression ne contient aucun point de séquence et donc le fait qu'elle consiste en deux modifications est suffisant pour rendre son comportement indéfini.

C++11 n'a pas de points de séquence et exige seulement que les modifications d'un objet soient ordonnées les unes par rapport aux autres et par rapport aux lectures du même objet pour produire un comportement défini.

Par conséquent, le comportement est indéfini en C++98 mais bien défini en C++11.


C++98

Clause C++98 [expr] 5 p4

Sauf indication contraire, l'ordre d'évaluation des opérandes des opérateurs individuels et des sous-expressions des expressions individuelles, ainsi que l'ordre dans lequel les effets secondaires se produisent, ne sont pas spécifiés.

Clause C++98 [expr.ass] 5.17 p1

Le résultat de l'opération d'assignation est la valeur stockée dans l'opérande de gauche après que l'assignation ait eu lieu ; le résultat est une lvalue.

Je crois donc que l'ordre est spécifié, cependant je ne vois pas que cela suffise à créer un point de séquence au milieu d'une expression. Et pour continuer avec la citation de [expr] 5 p4 :

Entre le point de séquence précédent et le suivant, la valeur stockée d'un objet scalaire doit être modifiée au maximum une fois par l'évaluation d'une expression.

Ainsi, même si l'ordre est spécifié, il me semble que cela n'est pas suffisant pour un comportement défini en C++98.


C++11

Le C++11 supprime les points de séquence au profit de l'idée beaucoup plus claire de séquence avant y séquencé-après . Le langage de C++98 est remplacé par

C++11 [intro.exécution] 1.9 p15

Sauf indication contraire, les évaluations des opérandes des opérateurs individuels et des sous-expressions des expressions individuelles ne sont pas séquencées. [...]

Si un effet secondaire sur un objet scalaire n'est pas séquencé par rapport à un autre effet secondaire sur le même objet scalaire ou un calcul de valeur utilisant la valeur du même objet scalaire, le comportement est indéfini.

C++11 [expr.ass] 5.17 p1

Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes de droite et de gauche, et avant le calcul de la valeur de l'expression d'affectation.

Ainsi, alors que le fait d'être ordonné n'était pas suffisant pour obtenir le comportement défini dans C++98, C++11 a modifié l'exigence de sorte que le fait d'être ordonné (c'est-à-dire séquencé) est suffisant.

(Et il me semble que la flexibilité supplémentaire offerte par "séquence avant" et "séquence après" a conduit à un langage beaucoup plus clair, cohérent et bien spécifié).


Il me semble peu probable qu'une implémentation de C++98 fasse quelque chose de surprenant lorsque la séquence d'opérations est bien spécifiée, même si cela est insuffisant pour produire un comportement techniquement bien défini. Par exemple, la représentation interne de cette expression produite par Clang en mode C++98 a un comportement bien défini et fait la chose attendue.

19voto

Mankarse Points 22800

Dans le langage C++11, l'expression est bien définie et donnera comme résultat i == 20 .

Desde [expr.ass]/1 :

Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes de droite et de gauche, et avant le calcul de la valeur de l'expression d'affectation.

Cela signifie que l'affectation i+=1 est séquencé avant le calcul de la valeur du côté gauche de (i+=10)+=10 qui est à son tour séquencé avant l'attribution finale à i .


En C++03, l'expression a un comportement non défini, parce qu'elle cause i pour être modifié deux fois sans point de séquence intermédiaire.

12voto

Fanael Points 4476

Peut-être. Cela dépend de la version C++.

En C++03, c'est un UB évident, il n'y a pas de point de séquence intermédiaire entre les affectations.

En C++11, comme l'explique Mankarse, ce n'est plus indéfini - l'affectation composée entre parenthèses est séquencée avant l'affectation externe, donc c'est correct.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X