相关疑难解决方法(0)

为什么这些构造使用前后增量未定义的行为?

#include <stdio.h>

int main(void)
{
   int i = 0;
   i = i++ + ++i;
   printf("%d\n", i); // 3

   i = 1;
   i = (i++);
   printf("%d\n", i); // 2 Should be 1, no ?

   volatile int u = 0;
   u = u++ + ++u;
   printf("%d\n", u); // 1

   u = 1;
   u = (u++);
   printf("%d\n", u); // 2 Should also be one, no ?

   register int v = 0;
   v = v++ + ++v;
   printf("%d\n", v); // 3 (Should be the …
Run Code Online (Sandbox Code Playgroud)

c increment operator-precedence undefined-behavior sequence-points

793
推荐指数
13
解决办法
7万
查看次数

是什么使i = i ++ + 1; 合法的C++ 17?

在开始大喊未定义的行为之前,这在N4659(C++ 17)中明确列出

  i = i++ + 1;        // the value of i is incremented
Run Code Online (Sandbox Code Playgroud)

然而在N3337(C++ 11)

  i = i++ + 1;        // the behavior is undefined
Run Code Online (Sandbox Code Playgroud)

改变了什么?

从我可以收集,从[N4659 basic.exec]

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的.[...]在运算符结果的值计算之前,对运算符的操作数的值计算进行排序.如果相对于同一存储器位置上的另一个副作用或者使用同一存储器位置中的任何对象的值进行的值计算,对存储器位置的副作用未被排序,并且它们不可能是并发的,则行为是未定义的.

其中定义为[N4659 basic.type]

对于简单的可复制类型,值表示是对象表示中的一组位,用于确定,该是实现定义的值集的一个离散元素

来自[N3337 basic.exec]

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的.[...]在运算符结果的值计算之前,对运算符的操作数的值计算进行排序.如果对标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义.

同样,值定义在[N3337 basic.type]

对于简单的可复制类型,值表示是对象表示中的一组位,用于确定,该是实现定义的值集的一个离散元素.

它们是相同的,除了提及无关紧要的并发性,并且使用内存位置而不是标量对象,其中

算术类型,枚举类型,指针类型,指向成员类型的指针std::nullptr_t以及这些类型的cv限定版本统称为标量类型.

这不会影响示例.

来自[N4659 expr.ass]

赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.右操作数在左操作数之前排序.

来自[N3337 expr.ass]

赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.

唯一的区别是N3337中没有最后一句话.

然而,最后一句话不应该具有任何重要性,因为左操作数i既不是"另一个副作用"也不是 …

c++ language-lawyer c++17

179
推荐指数
3
解决办法
1万
查看次数