Bevor Sie anfangen, undefiniertes Verhalten zu schreien, wird dies in N4659 (C ++ 17) explizit aufgeführt.
i = i++ + 1; // the value of i is incremented
Noch in N3337 (C ++ 11)
i = i++ + 1; // the behavior is undefined
Was hat sich geändert?
Soweit ich das beurteilen kann, aus [N4659 basic.exec]
Sofern nicht anders angegeben, werden Auswertungen von Operanden einzelner Operatoren und von Unterausdrücken einzelner Ausdrücke nicht sequenziert. [...] Die Wertberechnungen der Operanden eines Operators werden vor der Wertberechnung des Ergebnisses des Operators sequenziert. Wenn ein Nebeneffekt an einem Speicherort nicht in Bezug auf einen anderen Nebeneffekt an demselben Speicherort oder eine Wertberechnung unter Verwendung des Werts eines Objekts an demselben Speicherort sequenziert wird und diese möglicherweise nicht gleichzeitig auftreten, ist das Verhalten undefiniert.
Wobei der Wert unter [N4659 basic.type] definiert ist
Bei trivial kopierbaren Typen ist die Wertedarstellung eine Menge von Bits in der Objektdarstellung, die einen Wert bestimmt , der ein diskretes Element einer implementierungsdefinierten Menge von Werten ist
Sofern nicht anders angegeben, werden Auswertungen von Operanden einzelner Operatoren und von Unterausdrücken einzelner Ausdrücke nicht sequenziert. [...] Die Wertberechnungen der Operanden eines Operators werden vor der Wertberechnung des Ergebnisses des Operators sequenziert. Wenn eine Nebenwirkung auf ein Skalarobjekt in Bezug auf eine andere Nebenwirkung auf dasselbe Skalarobjekt oder eine Wertberechnung unter Verwendung des Werts desselben Skalarobjekts nicht sequenziert wird, ist das Verhalten undefiniert.
Ebenso ist der Wert unter [N3337 basic.type] definiert.
Bei trivial kopierbaren Typen ist die Wertdarstellung eine Menge von Bits in der Objektdarstellung, die einen Wert bestimmt , der ein diskretes Element eines implementierungsdefinierten Satzes von Werten ist.
Sie sind identisch, mit Ausnahme der Erwähnung der Parallelität, die keine Rolle spielt, und bei Verwendung des Speicherorts anstelle des skalaren Objekts , wo
Arithmetische Typen, Aufzählungstypen, Zeigertypen, Zeiger auf Elementtypen
std::nullptr_t
und lebenslaufqualifizierte Versionen dieser Typen werden gemeinsam als Skalartypen bezeichnet.
Was das Beispiel nicht beeinflusst.
Aus [N4659 expr.ass]
Der Zuweisungsoperator (=) und die zusammengesetzten Zuweisungsoperatoren gruppieren sich alle von rechts nach links. Alle benötigen einen modifizierbaren l-Wert als linken Operanden und geben einen l-Wert zurück, der sich auf den linken Operanden bezieht. Das Ergebnis ist in allen Fällen ein Bitfeld, wenn der linke Operand ein Bitfeld ist. In allen Fällen wird die Zuweisung nach der Wertberechnung des rechten und linken Operanden und vor der Wertberechnung des Zuweisungsausdrucks sequenziert. Der rechte Operand wird vor dem linken Operanden sequenziert.
Aus [N3337 expr.ass]
Der Zuweisungsoperator (=) und die zusammengesetzten Zuweisungsoperatoren gruppieren sich alle von rechts nach links. Alle benötigen einen modifizierbaren l-Wert als linken Operanden und geben einen l-Wert zurück, der sich auf den linken Operanden bezieht. Das Ergebnis ist in allen Fällen ein Bitfeld, wenn der linke Operand ein Bitfeld ist. In allen Fällen wird die Zuweisung nach der Wertberechnung des rechten und linken Operanden und vor der Wertberechnung des Zuweisungsausdrucks sequenziert.
Der einzige Unterschied besteht darin, dass der letzte Satz in N3337 fehlt.
Der letzte Satz sollte jedoch keine Bedeutung haben, da der linke Operand i
weder "ein weiterer Nebeneffekt" noch "den Wert desselben skalaren Objekts verwenden" ist, da der id-Ausdruck ein l-Wert ist.
i = i++ + 1;
.