赋值运算符不是序列点的任何好理由?

operator =不是序列点有什么好的理由吗? 在C和C ++中都有。

我很难想到一个反例。

按要求:

一般来说,事情需要成为序列点的理由。 他们不需要理由成为序列点; 这是默认值。

例如,由于短路行为, &&必须是一个序列点:如果左侧是假的,则不能评估右侧。 (这不仅仅是关于优化;右侧可能有副作用,和/或取决于左侧是真的,如在ptr && ptr->data 。)因此必须评估左侧首先,在右侧之前,为了看看是否应该评估右侧。

这个原因不存在=因为,尽管双方都有“评价”(虽然对双方都有什么不同的限制:左手边必须是左值 – l不能站立对于“左”,顺便说一句;它代表“位置”,如在记忆中的位置 – 我们不能分配给临时或文字),首先评估哪一方并不重要 – 只要双方都是在实际分配之前进行评估。

它是(有点)。 operator =(可以由工程师定义(也就是用户定义的operator = for class types))只是函数调用的语法糖。 因此,它具有与函数调用相同的“序列点”语义。

如果我们采用内置类型,那么我认为这是一件好事。
您不希望引入太多序列点,因为这会阻碍优化。

有许多理由不要求任何一方在另一方之前进行评估。 一个更有趣的问题是,在赋值算子本身做什么之前,是否需要对双方进行评估,并且需要副作用。 我建议这样的要求会减轻一些别名限制,但在某些情况下需要为编译器做更多的工作。 例如,假设“foo”和“bar”是指向地址重叠的大型结构的指针。 声明“* foo = * bar;” 将代表当前标准下的未定义行为。 如果在操作数的评估和赋值之间存在一个序列点,那么这样的语句将保证“工作”。 这样的保证对于赋值运算符将需要更复杂,即使在实践中指针永远不会重叠,也需要更大和更慢的代码。

例:

 unsigned char foo [100];
 typedef struct {int x,int y;} POINT;
 POINT * p1 =(POINT *)foo;
 POINT * p2 =(POINT *)(&(p1-> y));

鉴于上述声明,我认为以下语句具有指定的严格定义的行为,并且不涉及任何未定义的行为。

   p1-> y = somevalue;  //将p2-> x设置为某个值
   p2-> x = somevalue;  //将p1-> y设置为某个值
   * p1 = mystruct;  //将p2-> x设置为mystruct.y
   * p2 = mystruct;  //将p1-> x设置为mystruct.x

但是,以下两个语句将涉及未定义的行为:

   * p1 = * p2;
   * p2 = * p1;

如果在等号处有序列点,则编译器必须比较p1和p2,或者将源操作数复制到临时位置,然后将其复制到目标。 但是,该标准清楚地表明上述两个陈述都被认为是未定义的行为。 该标准要求编译器生成在将结构复制到非重叠结构时正常工作的代码,但对结构重叠时编译器可能执行的操作没有限制。 一个编译器,它会让处理器进入循环发送“Frink Rules!” 通过这样做,每个打开的TCP套接字都不会违反标准。