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