Подтвердить что ты не робот

Любая хорошая причина, почему оператор присваивания не является точкой последовательности?

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

У меня проблемы с контр-примером.

4b9b3361

Ответ 1

По запросу:

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

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

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

Ответ 2

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

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

Ответ 3

Существует много причин не требовать, чтобы обе стороны оценивались друг перед другом. Более интересный вопрос заключается в том, требуется ли оценка обеих сторон с побочными эффектами, прежде чем сам оператор присваивания сделает что-либо. Я бы предположил, что такое требование облегчит некоторые ограничения на псевдонимы, но в некоторых случаях потребует больше работы для компилятора. Например, предположим, что "foo" и "bar" являются указателями на большие структуры, адреса которых будут перекрываться. Утверждение "* foo = * bar;" будет представлять поведение undefined в соответствии с текущим стандартом. Если бы между оценкой операндов и назначением была точка последовательности, такое утверждение было бы гарантировано "работать". Такая гарантия потребовала бы более сложного для оператора присваивания, требующего большего и более медленного кода, даже если на практике указатели никогда не будут перекрываться.

Пример:

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

Учитывая приведенные выше объявления, я думаю, что следующие утверждения имеют строго определенные типы поведения, указанные и не содержат никакого поведения undefined.

  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

Однако следующие два утверждения включают undefined Поведение:

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

Если бы на знаке равенства была точка последовательности, компилятор должен был бы либо сравнить p1 и p2, либо скопировать исходный операнд в временное местоположение, а затем скопировать его в пункт назначения. Стандарт, однако, дает понять, что вышеупомянутые два утверждения считаются undefined Behavior. Стандарт требует, чтобы компиляторы генерировали код, который работает правильно при копировании структуры в неперекрывающуюся структуру, но не создает ограничений на то, что могут делать компиляторы, если структуры перекрываются. Компилятор, который обработал бы процессор в цикле, отправляя "Правила Флинка!". каждый открытый TCP-сокет не будет нарушать стандарт таким образом.