在你开始对未定义的行为大喊大叫之前,这是在 N4659(C++17)中显示列出的
i = i++ + 1; // the value of i is incremented
i = i++ + 1; // the behavior is undefined
什么变了?
据我所知,从[N4659 Basic.Exec]
除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是未排序的。[..]运算符的操作数的值计算在运算符结果的值计算之前排序。如果内存位置上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中的任何对象的值进行的值计算是无序的,并且它们不是潜在并发的,则该行为是未定义的。
其中,价值在[N4659基本类型]处定义
对于普通可复制类型,值表示是对象表示中确定价值的一组位,它是实现定义的一组值中的一个离散元素
除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是未排序的。[..]运算符的操作数的值计算在运算符结果的值计算之前排序。如果标量对象上的副作用相对于同一标量对象上的另一副作用或使用同一标量对象值的值计算是未排序的,则行为是未定义的。
同样,在[N3337基本类型]处定义值
对于普通可复制类型,值表示是对象表示中的一组位,用于确定价值,这是实现定义的一组值中的一个离散元素。
它们是相同的,只是提到了并发性(这无关紧要),并且使用了内存位置而不是标量对象,其中
算术类型、枚举类型、指针类型、指向成员类型的指针、
std::nullptr_t
以及这些类型的cv限定版本统称为标量类型。
这并不影响示例。
赋值运算符(=)和复合赋值运算符都从右向左分组。它们都需要一个可修改的左值作为左操作数,并返回一个引用左操作数的左值。如果左操作数是位域,则所有情况下的结果都是位域。在所有情况下,赋值都在右操作数和左操作数的值计算之后、赋值表达式的值计算之前排序。右操作数的顺序在左操作数之前。
赋值运算符(=)和复合赋值运算符都从右向左分组。它们都需要一个可修改的左值作为左操作数,并返回一个引用左操作数的左值。如果左操作数是位域,则所有情况下的结果都是位域。在所有情况下,赋值都在右操作数和左操作数的值计算之后、赋值表达式的值计算之前排序。
唯一的区别是N3337中没有最后一句话。
然而,最后一句不应该有任何重要性,因为左操作数i
既不是“另一个副作用”,也不是“使用同一标量对象的值”,因为ID-表达式是左值。