Любая хорошая причина, почему оператор присваивания не является точкой последовательности?
Есть ли веская причина для operator =
не быть точкой последовательности? И в C и С++.
У меня проблемы с контр-примером.
Ответы
Ответ 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-сокет не будет нарушать стандарт таким образом.