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

时间:2010-12-06 01:30:17

标签: c++ c sequence-points

operator =有没有充分理由不成为序列点?在C和C ++中都有。

我无法考虑反例。

3 个答案:

答案 0 :(得分:21)

按要求:

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

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

=不存在这个原因,因为虽然双方都有“评价”(尽管双方都有不同的限制:左侧必须是左值 - l不代表“左”,顺便说一句;它代表“位置”,如在内存中的位置 - 我们不能分配给临时或文字),无关紧要首先评估方 - 只要在实际分配之前评估双方。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

有许多理由不要求任何一方在另一方之前进行评估。一个更有趣的问题是,在赋值算子本身做任何事情之前,是否需要对双方进行评估,并且需要副作用。我建议这样的要求会减轻一些别名限制,但在某些情况下需要为编译器做更多的工作。例如,假设“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;  // Sets p2->x to somevalue
  p2->x = somevalue;  // Sets p1->y to somevalue
  *p1 = mystruct;     // Sets p2->x to mystruct.y
  *p2 = mystruct;     // Sets p1->x to mystruct.x

然而,以下两个陈述将涉及未定义的行为:

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

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