Есть веская причина, почему оператор присваивания не является точкой последовательности? - PullRequest
19 голосов
/ 06 декабря 2010

Есть ли веская причина для того, чтобы operator = не был точкой последовательности? Как в C, так и в C ++.

Мне сложно подумать о контрпримере.

Ответы [ 3 ]

21 голосов
/ 06 декабря 2010

По заказу:

В общем, вещам нужна причина, чтобы была точкой последовательности. Им не нужна причина , а не , чтобы быть точкой последовательности; это по умолчанию.

Например, && должно быть точкой последовательности из-за короткого замыкания: если левая сторона ложна, правая сторона не должна оцениваться . (Это не просто оптимизация; правая сторона может иметь побочные эффекты и / или зависеть от истинности левой стороны, как в ptr && ptr->data.) Поэтому левая сторона должна быть оценена первой, перед с правой стороны, чтобы увидеть, должна ли вообще оцениваться правая сторона.

Эта причина не существует для =, потому что, хотя есть «оценка» для обеих сторон (хотя существуют разные ограничения на то, что может появиться с обеих сторон: левая сторона должна быть lvalue - l не означает «левый», кстати, он означает «местоположение», как в расположении в памяти - мы не можем назначить временное или литеральное значение), не имеет значения, какая сторона вычисляется первой - до тех пор, пока обе стороны оцениваются до фактического назначения.

0 голосов
/ 09 декабря 2010

Есть много причин не требовать, чтобы одна из сторон была оценена раньше другой. Более интересным вопросом является необходимость оценки обеих сторон в сочетании с побочными эффектами, прежде чем сам оператор присваивания что-либо предпримет. Я бы предположил, что такое требование ослабит некоторые ограничения псевдонимов, но в некоторых случаях потребует больше работы для компилятора. Например, предположим, что «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-сокет не будет нарушать стандарт.

0 голосов
/ 06 декабря 2010

Это (вроде). Оператор = (который может быть определен инженером (он же пользовательский оператор = для типов классов)) является просто синтаксическим сахаром для вызова функции. В результате он имеет ту же семантику «точки последовательности», что и вызов функции.

Если мы говорим о встроенных типах, то я думаю, что это хорошо.
Вы не хотите вводить слишком много точек последовательности, так как это мешает оптимизации.

...