Почему мы не можем создавать тривиально конструируемые объекты, используя malloc, если тривиальный конструктор по умолчанию не выполняет никаких действий?


14

Мне трудно понять следующий абзац, цитируемый из cppreference о тривиальном конструкторе по умолчанию. Я искал stackoverflow, но все еще не получил четкого ответа. Поэтому, пожалуйста, помогите.

Тривиальный конструктор по умолчанию - это конструктор, который не выполняет никаких действий. Все типы данных, совместимые с языком C (типы POD), легко конструируются по умолчанию. В отличие от C, однако, объекты с тривиальными конструкторами по умолчанию не могут быть созданы путем простой интерпретации соответствующим образом выровненного хранилища, такого как память, выделенная с помощью std :: malloc :place-new, требуется для формального введения нового объекта и предотвращения возможного неопределенного поведения.

В частности, если простой конструктор по умолчанию ничего не делает, почему мы не можем интерпретировать хранилище и делать вид, что существует объект с данным типом? Не могли бы вы привести несколько примеров возможного неопределенного поведения, которое это может вызвать?


Самая важная задача компилятора - не компилировать исходный код, а отклонить, возможно, неверный код. Это не может сделать это, когда вы используете malloc ().
Ганс

6
Причина очень проста. Чем меньше возможностей для программиста делать сумасшедшие вещи, тем больше возможностей для компилятора делать сумасшедшие вещи (агрессивная оптимизация).
нет. местоимения м.

1
По тем же причинам, что вы не можете просто *reinterpret_cast<float*>(&someNonFloatObject) = 0.1f;. C ++ имеет концепцию объектов и времени жизни объектов, заданную на абстрактной машине, и тот факт, что отсутствует инструкция ЦП для создания объекта из хранилища, не означает, что на абстрактной машине нет различий.
Макс Лангхоф

1
@HansPassant Компилятор, который отклоняет весь код, отклоняет весь неверный код. В любом случае, работа копилера не состоит в том, чтобы отклонять программы, которые имеют UB.
нет. местоимения м.

Ответы:


7

P0593R5 дает этот пример:

struct X { int a, b; };
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

и объясняет:

При компиляции с помощью компилятора C ++ этот код имеет неопределенное поведение, поскольку p-> пытается выполнить запись в подобъект int объекта X, и эта программа никогда не создавала ни объект X, ни подобъект int.

За [intro.object] p1,

Объект создается по определению, выражению new, при неявном изменении активного члена объединения или при создании временного объекта.

... и эта программа не сделала ничего из этого.

На практике это работает, и ситуация с UB рассматривается скорее как дефект в стандарте, чем что-либо еще. Вся цель статьи - предложить способ решения этой проблемы и подобных случаев, не ломая другие вещи.


1

По причине "чистоты".

Альтернативой и фактическим статус-кво было то, что каждый регион хранения будет содержать все объекты, помещающиеся в это хранилище, одновременно. Некоторым членам комитета не нравится статус-кво, и многие опасались, что в одном и том же месте может быть бесконечно много объектов (в виртуальном, неинициализированном состоянии).

Никто никогда не был в состоянии показать логическую проблему с бесконечным количеством объектов в области хранения.

Поскольку у них были разные разделы стандарта, говорящие противоречащие друг другу вещи, члены комитета просто решили серьезно отнестись к одной из худших частей стандарта.

Кроме того, использование строковых литералов строго запрещено, если вы действительно серьезно относитесь к этой части стандарта.


использование строковых литералов строго запрещено . Есть похожая проблема CWG об type_infoобъектах. Вы сообщили о строковых литералах?
Языковой адвокат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.