En parcourant le code d'un projet, je suis tombé sur la déclaration suivante:
++x %= 10;
Cette déclaration est-elle bien définie en C++ ou tombe-t-elle dans la même catégorie que
a[i] = i++
?
Selon C++ 11 1.9 Program execution /15
:
Sauf indication contraire, les évaluations d'opérandes d'opérateurs individuels et de sous-expressions d'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 n'est pas défini.
Dans ce cas, je crois que ++x
Est un effet secondaire et x %= 10
Est un calcul de valeur, donc on pourrait penser que ce serait un comportement indéfini. Cependant, la section d'affectation (5.17 /1
) A ceci à dire (mon gras):
Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes droit et gauche, et avant le calcul de la valeur de l'expression d'affectation.
Cela signifie donc que les deux parties sont séquencées avant l'affectation et avant que le résultat de l'affectation ne soit rendu disponible. Et puisque la norme indique également (5.17 /7
) Que x OP = y
Est identique à x = x OP y
Mais que x
n'est évalué qu'une seule fois, cela s'avère est un comportement bien défini, car il équivaut à:
++x = Q % 10; // where Q is the value from ++x, not evaluated again.
La seule question qui reste est alors de savoir quel côté de l'affectation est évalué car ils sont non séquencé. Cependant, je ne pense pas que cela soit important dans ce cas, car les deux auront le même effet:
++x = Q % 10; // LHS evaluated first
Q = ++x % 10; // RHS evaluated first
Maintenant, c'est ma lecture de la norme. Bien que j'ai une bonne expérience dans le décodage de documents complexes, il peut y avoir quelque chose que j'ai manqué - je ne pense pas parce que nous avons tous ont eu une discussion animée ici pour arriver à ce point :-) et je pense que nous avons tous établi les sections pertinentes.
Mais, qu'il soit bien défini ou non, les codeurs décents ne devraient pas écrire du code comme ça. Cela fait longtemps depuis les jours de faible mémoire/petit stockage des minis PDP, il est temps que nous écrivions notre code pour être lisible.
Si vous voulez incrémenter alors prenez le modulo, utilisez x = (x + 1) % 10
, ne serait-ce que pour le rendre plus facile à comprendre pour le prochain pauvre Joe qui lira ce code.
TL; DR: Il est bien défini car x
est garanti d'être incrémenté avant l'affectation.
[intro.execution]/15:
Sauf indication contraire, les évaluations d'opérandes d'opérateurs individuels et de sous-expressions d'expressions individuelles ne sont pas séquencées.
Cependant, [expr.ass]/1 note quelque chose:
Dans tous les cas, l'affectation est séquencée après le calcul de la valeur des opérandes droit et gauche, et avant le calcul de la valeur de l'expression d'affectation.
Cela constitue donc une exception à la première citation. De plus, comme indiqué dans [expr.pre.incr]1, ++x
est équivalent à x += 1
, qui est également couvert par la citation ci-dessus: l'affectation est séquencée avant le calcul de la valeur. Ainsi pour ++x
l'incrément est séquencé avant le calcul de la valeur.
Compte tenu de cela, il n'est pas difficile de voir que dans ++x %= 10
, l'incrémentation est effectuée avant l'affectation.
Ainsi, l'effet secondaire d'incrémentation est séquencé avant l'effet secondaire d'affectation, et donc tous les effets secondaires impliqués sont séquencés.
Pour le dire autrement, la norme impose le séquençage suivant:
++x
et 10
sont évalués - dont l'ordre n'est pas séquencé, mais 10
n'est qu'un littéral, donc ce n'est pas pertinent ici. ++x
est évalué: x
est incrémentée.x
.x
est prise modulo 10
et affecté à x
.Par conséquent
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 n'est pas défini.
ne s'applique pas comme les effets secondaires et les calculs de valeur sont séquencés.
1 Pas [expr.post.incr] qui serait pour l'incrément de suffixe!
Regardons l'opérateur d'incrémentation unaire:
5.3.2 Incrémenter et décrémenter [expr.pre.incr]
1 L'opérande du préfixe
++
est modifié en ajoutant 1, ou défini surtrue
s'il estbool
(cette utilisation est déconseillée). L'opérande doit être une valeur l modifiable. Le type de l'opérande doit être un type arithmétique ou un pointeur vers un type d'objet complètement défini. Le résultat est l'opérande mis à jour; c'est une valeur l, et c'est un champ de bits si l'opérande est un champ de bits. Six
n'est pas de typebool
, l'expression++x
est équivalent àx+=1
.
[...]
Ainsi, toutes les évaluations et les effets secondaires concernant cet opérateur unaire sont planifiés avant sa valeur et ne peuvent donc pas causer de ravages.
Il ne reste plus qu'à évaluer %= 10
sur cette valeur. Seule l'évaluation de la constante peut être simultanée (ce qui ne pourrait pas faire de mal), le reste est strictement séquencé après tout le reste.
Je vais proposer une réponse alternative, sans citer le bon livre, car je pense que le réécrire légèrement le rend évident.
++x %= 10; // as stated
x += 1 %= 10; // re-write the 'sugared' ++x
Cela est assez clair à mes yeux. Comme nous le savons, le résultat de la mission (qui si nous voulons vraiment, le toujours "sucré" +=
se réduit à) est lui-même une valeur l, il ne devrait donc pas y avoir de doute que, par une réduction supplémentaire, l'expression est:
(x = x+1) %= 10 // += -> =1+
x = (x+1) % 10 // assignment returns reference to incremented x
Dans l'expression
++x %= 10;
la partie la plus déroutante est que x
modifie deux fois entre deux points de séquence, une fois par le préfixe ++
et une fois par affectation du résultat. Cela donne l'impression que l'expression ci-dessus invoque un comportement indéfini comme une fois que nous avons appris dans l'ancien C++ que
Entre le point de séquence précédent et suivant, un objet scalaire doit voir sa valeur stockée modifiée au plus une fois par l'évaluation d'une expression.
En C++ 11, la règle est différente (il s'agit de séquencer au lieu de points de séquence !):
Si un effet secondaire sur un objet scalaire est non 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 n'est pas défini.
Comme nous savons déjà que ++x
est l'expression ci-dessus ne sera évaluée qu'une seule fois (et donnera une valeur l), car
Le comportement d'une expression de la forme
E1 op = E2
est équivalent àE1 = E1 op E2
exceptéE1
n'est évalué qu'une seule fois.
et savent également que l'évaluation des opérandes ++x
et 10
aura lieu avant le calcul du résultat de %=
opérateur standard:
Les calculs de valeur des opérandes d'un opérateur sont séquencés avant le calcul de valeur du résultat de l'opérateur.
Conclusion:
++x
ne sera évalué qu'une seule fois en donnant une valeur l et seulement après cela %=
l'opération sera effectuée. Cela signifie que les deux modifications de x
sont séquencées et que l'expression ci-dessus est bien définie.
Eh bien, c'est défini. Comme comportement indéfini:
§1.9/15:
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 n'est pas défini.
Il y a ici deux effets secondaires non séquencés.
Étant donné l'expression ++x %= 10;
en se déplaçant de gauche à droite, on a:
(x+1)
=
, un péché x = x + 1
), par exemple. a effet secondaire par §1.9/12%=
) qui a lui-même à la fois un calcul de valeur ('%
') et une modification d'objet effet secondaire (' =
') (ibid from 1,2) sur le même objet scalaire (' x
').Les deux sous-expressions dans l'expression complète ne sont pas séquencées l'une par rapport à l'autre. Bien que nous ayons commencé par le lire de gauche à droite, ce sont séquencés de façon indéterminée, il n'y a donc explicitement aucun renflouement d'ordre partiel du §1.9/13:
Compte tenu de deux évaluations A et B , si A est séquencé avant B , puis l'exécution de A doit précéder l'exécution de B . Si A n'est pas séquencé avant B et B n'est pas séquencé avant A , puis A et B sont sans séquence.
Donc, UDB.
L'incrément de préfixe (++ x) a la priorité la plus élevée par l'affectation du module (% =). L'instruction: ++ x% = 10; peut s'exprimer comme suit:
++x;
x%= 10;