Prima di iniziare a urlare comportamenti indefiniti, questo è elencato esplicitamente in N4659 (C ++ 17)
i = i++ + 1; // the value of i is incremented
Eppure in N3337 (C ++ 11)
i = i++ + 1; // the behavior is undefined
Che cosa è cambiato?
Da quello che posso raccogliere, da [N4659 basic.exec]
Salvo dove indicato, le valutazioni degli operandi dei singoli operatori e delle sottoespressioni delle singole espressioni non sono seguite. [...] I calcoli del valore degli operandi di un operatore sono sequenziati prima del calcolo del valore del risultato dell'operatore. Se un effetto collaterale su una posizione di memoria non è seguito rispetto a un altro effetto collaterale sulla stessa posizione di memoria o a un calcolo del valore che utilizza il valore di qualsiasi oggetto nella stessa posizione di memoria e che non sono potenzialmente concorrenti, il comportamento non è definito.
Dove il valore è definito in [N4659 basic.type]
Per tipi banalmente copiabili, la rappresentazione del valore è un insieme di bit nella rappresentazione dell'oggetto che determina un valore , che è un elemento discreto di un insieme di valori definito dall'implementazione
Salvo dove indicato, le valutazioni degli operandi dei singoli operatori e delle sottoespressioni delle singole espressioni non sono seguite. [...] I calcoli del valore degli operandi di un operatore sono sequenziati prima del calcolo del valore del risultato dell'operatore. Se un effetto collaterale su un oggetto scalare non è seguito rispetto a un altro effetto collaterale sullo stesso oggetto scalare o a un calcolo del valore che utilizza il valore dello stesso oggetto scalare, il comportamento non è definito.
Allo stesso modo, il valore è definito in [N3337 basic.type]
Per tipi banalmente copiabili, la rappresentazione del valore è un insieme di bit nella rappresentazione dell'oggetto che determina un valore , che è un elemento discreto di un insieme di valori definito dall'implementazione.
Sono identici ad eccezione della menzione di concorrenza che non ha importanza e dell'uso della posizione della memoria anziché dell'oggetto scalare , dove
I tipi aritmetici, i tipi di enumerazione, i tipi di puntatori, i puntatori ai tipi di membri
std::nullptr_t
e le versioni qualificate per cv di questi tipi sono chiamati collettivamente tipi scalari.
Il che non influisce sull'esempio.
L'operatore di assegnazione (=) e gli operatori di assegnazione composti raggruppano tutti da destra a sinistra. Tutti richiedono un valore modificabile come operando sinistro e restituiscono un valore riferito all'operando sinistro. Il risultato in tutti i casi è un campo bit se l'operando di sinistra è un campo bit. In tutti i casi, l'assegnazione viene eseguita in sequenza dopo il calcolo del valore degli operandi destro e sinistro e prima del calcolo del valore dell'espressione di assegnazione. L'operando di destra è sequenziato prima dell'operando di sinistra.
L'operatore di assegnazione (=) e gli operatori di assegnazione composti raggruppano tutti da destra a sinistra. Tutti richiedono un valore modificabile come operando sinistro e restituiscono un valore riferito all'operando sinistro. Il risultato in tutti i casi è un campo bit se l'operando di sinistra è un campo bit. In tutti i casi, l'assegnazione viene eseguita in sequenza dopo il calcolo del valore degli operandi destro e sinistro e prima del calcolo del valore dell'espressione di assegnazione.
L'unica differenza è che l'ultima frase è assente in N3337.
L'ultima frase, tuttavia, non dovrebbe avere alcuna importanza in quanto l'operando di sinistra i
non è né "un altro effetto collaterale" né "usa il valore dello stesso oggetto scalare" poiché l' espressione id è un valore.
i = i++ + 1;
.