Avant de commencer à crier un comportement indéfini, cela est explicitement répertorié dans N4659 (C ++ 17)
i = i++ + 1; // the value of i is incremented
Pourtant dans N3337 (C ++ 11)
i = i++ + 1; // the behavior is undefined
Qu'est ce qui a changé?
D'après ce que je peux comprendre , à partir de [N4659 basic.exec]
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. [...] 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 mémoire n'est pas séquencé par rapport à un autre effet secondaire sur le même emplacement mémoire ou à un calcul de valeur utilisant la valeur de n'importe quel objet dans le même emplacement mémoire, et qu'ils ne sont pas potentiellement simultanés, le comportement n'est pas défini.
Où la valeur est définie dans [N4659 basic.type]
Pour les types trivialement copiables, la représentation de valeur est un ensemble de bits dans la représentation d'objet qui détermine une valeur , qui est un élément discret d'un ensemble de valeurs défini par l'implémentation
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. [...] 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 n'est pas défini.
De même, la valeur est définie dans [N3337 basic.type]
Pour les types facilement copiables, la représentation de valeur est un ensemble de bits dans la représentation d'objet qui détermine une valeur , qui est un élément discret d'un ensemble de valeurs défini par l'implémentation.
Ils sont identiques sauf mention de la concurrence qui n'a pas d'importance, et avec l'utilisation de l' emplacement mémoire au lieu de l' objet scalaire , où
Les types arithmétiques, les types d'énumération, les types de pointeur, le pointeur vers les types de membre
std::nullptr_t
et les versions qualifiées cv de ces types sont collectivement appelés types scalaires.
Ce qui n'affecte pas l'exemple.
L'opérateur d'affectation (=) et les opérateurs d'affectation composés sont tous regroupés de droite à gauche. Tous nécessitent une lvalue modifiable comme opérande gauche et renvoient une lvalue faisant référence à l'opérande gauche. Le résultat dans tous les cas 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 droit et gauche et avant le calcul de la valeur de l'expression d'affectation. L'opérande droit est séquencé avant l'opérande gauche.
L'opérateur d'affectation (=) et les opérateurs d'affectation composés sont tous regroupés de droite à gauche. Tous nécessitent une lvalue modifiable comme opérande gauche et renvoient une lvalue faisant référence à l'opérande gauche. Le résultat dans tous les cas 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 droit et gauche et avant le calcul de la valeur de l'expression d'affectation.
La seule différence étant la dernière phrase étant absente de N3337.
Cependant, la dernière phrase ne devrait pas avoir d'importance car l'opérande de gauche i
n'est ni "un autre effet secondaire" ni "l'utilisation de la valeur du même objet scalaire" car l' expression id est une lvalue.
i = i++ + 1;
.