Есть ли веская причина для operator =
не быть точкой последовательности? И в C и С++.
У меня проблемы с контр-примером.
Есть ли веская причина для operator =
не быть точкой последовательности? И в C и С++.
У меня проблемы с контр-примером.
По запросу:
В общем, для того, чтобы быть нужна точка последовательности, нужна причина. Им не нужна причина не как точка последовательности; что по умолчанию.
Например, &&
должна быть точкой последовательности из-за короткого замыкания: если левая сторона ложна, правая сторона не должна оцениваться. (Это не просто оптимизация, а правая сторона может иметь побочные эффекты и/или быть в зависимости от того, что левая сторона истинна, как в ptr && ptr->data
.) Поэтому левую сторону нужно оценить сначала, прежде правую часть, чтобы проверить, должна ли вообще быть оценена правая часть.
Эта причина не существует для =
, потому что, хотя для обеих сторон есть "оценка" (хотя существуют разные ограничения на то, что может появляться с обеих сторон: левая сторона должна быть lvalue - l
не означает "left", btw, это означает "местоположение", как и в месте в памяти - мы не можем назначить временный или литерал), неважно, какая сторона оценивается в первую очередь - до тех пор, пока обе стороны оцениваются до фактического назначения.
Это (вроде). Оператор = (который может быть определен инженером (так называемый пользовательский оператор = для типов классов)) является просто синтаксическим сахаром для вызова функции. В результате он имеет ту же "семантику точки последовательности", что и вызов функции.
Если мы занимаемся построенными типами, я думаю, что это хорошо.
Вы не хотите вводить слишком много точек последовательности, поскольку это препятствует оптимизации.
Существует много причин не требовать, чтобы обе стороны оценивались друг перед другом. Более интересный вопрос заключается в том, требуется ли оценка обеих сторон с побочными эффектами, прежде чем сам оператор присваивания сделает что-либо. Я бы предположил, что такое требование облегчит некоторые ограничения на псевдонимы, но в некоторых случаях потребует больше работы для компилятора. Например, предположим, что "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-сокет не будет нарушать стандарт таким образом.