Avant que vous ne commenciez à crier au comportement indéfini, c'est explicitement répertorié dans N4659 (C++17)
i = i++ + 1; // the value of i is incremented
Pourtant, en N3337 (C++11)
i = i++ + 1; // the behavior is undefined
Qu'est-ce qui a changé ?
D'après ce que je peux comprendre, de [N4659 basic.exec]
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. [...] 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. Si un effet secondaire sur un emplacement de mémoire n'est pas séquencé par rapport à un autre effet secondaire sur le même emplacement de mémoire ou à un calcul de valeur utilisant la valeur d'un objet quelconque dans le même emplacement de mémoire, et s'ils ne sont pas potentiellement concurrents, le comportement est indéfini.
Dónde valeur est défini à [N4659 type.de.base]
Pour les types trivialement copiables, la représentation de la valeur est un ensemble de bits dans la représentation de l'objet qui détermine un valeur qui est un élément discret d'un ensemble de valeurs défini par la mise en œuvre.
Desde [N3337 basic.exec]
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. [...] 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. 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.
De même, la valeur est définie à [N3337 basic.type]
Pour les types trivialement copiables, la représentation de la valeur est un ensemble de bits dans la représentation de l'objet qui détermine un valeur qui est un élément discret d'un ensemble de valeurs défini par la mise en œuvre.
Ils sont identiques, à l'exception de la mention de la concurrence, qui n'a pas d'importance, et de l'utilisation de l'attribut emplacement de mémoire au lieu de objet scalaire où
Types arithmétiques, types d'énumération, types de pointeurs, types de pointeurs vers les membres,
std::nullptr_t
Les versions qualifiées cv de ces types sont collectivement appelées types scalaires.
Ce qui n'affecte pas l'exemple.
Desde [N4659 expr.ass]
L'opérateur d'affectation (=) et les opérateurs d'affectation composés regroupent tous de droite à gauche. Ils requièrent tous une lvalue modifiable comme opérande gauche et retournent une lvalue faisant référence à l'opérande gauche. Dans tous les cas, le résultat est un champ de bits si l'opérande de gauche est un champ de bits. 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. L'opérande de droite est séquencé avant l'opérande de gauche.
Desde [N3337 expr.ass]
L'opérateur d'affectation (=) et les opérateurs d'affectation composés regroupent tous de droite à gauche. Ils requièrent tous une lvalue modifiable comme opérande gauche et retournent une lvalue faisant référence à l'opérande gauche. Dans tous les cas, le résultat est un champ de bits si l'opérande de gauche est un champ de bits. 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.
La seule différence est que la dernière phrase est absente dans N3337.
La dernière phrase, cependant, ne devrait pas avoir d'importance car l'opérande gauche i
n'est ni "un autre effet secondaire" ni "en utilisant la valeur du même objet scalaire" comme le id-expression est une lvalue.
23 votes
Vous avez identifié la raison pour laquelle : En C++17, l'opérande de droite est séquencé avant l'opérande de gauche. En C++11, cette séquence n'existait pas. Quelle est, précisément, votre question ?
4 votes
@Rob Voir la dernière phrase.
0 votes
Il y a deux effets secondaires non séquencés, tous deux écrits :
i =
a pour effet secondaire d'écrire dansi
.i++
a pour effet secondaire d'écrire dansi
.0 votes
@Rob S'ils sont non séquencés, cela rendrait l'exemple de la norme tellement faux que j'hésiterais beaucoup à affirmer qu'ils sont non séquencés.
1 votes
Ils sont non séquencés dans C++11, séquencés dans C++17, d'où les différents exemples.
7 votes
Quelqu'un a-t-il un lien vers la motivation de ce changement ? J'aimerais qu'un analyseur statique soit capable de dire "vous ne voulez pas faire ça" lorsqu'il est confronté à un code tel que
i = i++ + 1;
.8 votes
NeilButterworth, c'est dans le journal. p0145r3.pdf : "Raffiner l'ordre d'évaluation des expressions pour le C++ idiomatique".
0 votes
@xaizek Quelle partie de cela, exactement - je n'ai rien vu de pertinent, mais c'est peut-être ma faute.
0 votes
@xaizek - En jetant un coup d'œil rapide à ce document, la section 5 implique qu'elle n'a pas couvrir le cas de
++
.10 votes
@NeilButterworth, la section numéro 2 dit que c'est contre intuitif et que même les experts ne parviennent pas à faire la bonne chose dans tous les cas. C'est à peu près toute leur motivation.
1 votes
@OliverCharlesworth, le document ajoute cette formulation à propos de l'opérateur d'affectation. Cela ne fonctionne pas sur les expressions unaires dans tous les contextes, mais affecte la façon dont elles sont traitées dans le cadre de l'affectation comme l'explique la réponse acceptée.
0 votes
Je dirais que tout compilateur qui ne peut pas lire ça est bogué.
0 votes
N4700 est une version plus récente de C++17.
0 votes
Alors, que se passe-t-il quand on fait "c=c++ +1 ;"? Est-ce la même chose que "c=c+2" ?
0 votes
@nick012000 ce serait c=c+1, puisque la valeur produite par c++ + 1 est c+1.
0 votes
@nick012000 La première ligne de code de l'OP mentionne ce que cela fait. Bien qu'à mon avis, le commentaire aurait dû dire "obtient 1 ajouté à celui-ci" plutôt que "est incrémenté".
0 votes
@Rob Des questions comme celles-ci sont la raison d'être de cette proposition : open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0431r0.htm mais bien sûr, elle n'a pas été défendue correctement lors des réunions du comité et a probablement connu une mort lente.