operator =
不是序列点有什么好的理由吗?在 C 和 C++ 中。
我很难想出一个反例。
按要求:
一般来说,事物需要一个理由才能成为一个序列点。他们不需要理由不成为序列点;这是默认设置。
例如,&&
由于短路行为,必须是序列点:如果左侧为假,则不得评估右侧。(这不仅仅是关于优化;右侧可能有副作用,和/或取决于左侧是否为真,如ptr && ptr->data
.)因此必须先评估左侧,然后再评估右侧 -手边,以查看是否应该评估右手边。
这个原因不存在的=
原因是,尽管双方都要做“评估”(尽管对双方可以出现的内容有不同的限制:左侧必须是左值 -l
不代表“左”,顺便说一句;它代表“位置”,如内存中的位置 - 我们不能分配给临时或文字),首先评估哪一方并不重要 - 只要双方都在之前评估实际分配。
它是(有点)。operator=(可以由工程师定义(又名用户定义的类类型的 operator=))只是函数调用的语法糖。因此,它具有与函数调用相同的“序列点”语义。
如果我们正在考虑内置类型,那么我认为这是一件好事。
您不想引入太多序列点,因为这会阻碍优化。
有很多理由不要求任何一方先于另一方进行评估。一个更有趣的问题是,在赋值运算符本身做任何事情之前,是否需要对双方进行评估,包括副作用。我建议这样的要求会减轻一些别名限制,但在某些情况下需要编译器做更多的工作。例如,假设“foo”和“bar”是指向地址会重叠的大型结构的指针。语句“*foo = *bar;” 将代表当前标准下的未定义行为。如果在操作数的计算和赋值之间有一个序列点,那么这样的语句将保证“工作”。这样的保证对于赋值运算符来说需要更复杂的,
例子:
无符号字符 foo[100]; typedef struct {int x, int y;} POINT; 点 *p1 = (点*)foo; 点 *p2 = (点*)(&(p1->y));
鉴于上述声明,我认为以下陈述具有严格定义的行为,不涉及任何未定义的行为。
p1->y = 某个值;// 将 p2->x 设置为某个值 p2->x = 某个值;// 将 p1->y 设置为某个值 *p1 = mystruct; // 将 p2->x 设置为 mystruct.y *p2 = mystruct; // 将 p1->x 设置为 mystruct.x
但是,以下两个语句将涉及未定义的行为:
*p1 = *p2; *p2 = *p1;
如果等号处有序列点,编译器将不得不比较 p1 和 p2,或者将源操作数复制到临时位置,然后将其复制到目标位置。然而,该标准清楚地表明,上述两个语句都被认为是未定义的行为。该标准要求编译器生成在将结构复制到非重叠结构时可以正常工作的代码,但对结构重叠时编译器可以执行的操作没有任何限制。一个编译器,它将处理器进入循环发送“Frink 规则!” 这样做不会违反标准。