Управление тривиальными типами

Я понял, что тонкости тривиальных типов на С++ нетривиальны для понимания и надеюсь, что кто-то может просветить меня следующим образом.

Данный тип T, хранилище для T, выделенное с помощью ::operator new(std::size_t) или ::operator new[](std::size_t) или std::aligned_storage, и void * p, указывающее на местоположение в этом хранилище, подходящим образом выровненное для T, чтобы оно могло построим в точке p:

  • Если std::is_trivially_default_constructible<T>::value имеет значение, это код, вызывающий поведение undefined, когда код пропускает инициализацию T в p (т.е. с помощью T * tPtr = new (p) T();), прежде чем обратится к *p как T? Можно ли использовать T * tPtr = static_cast<T *>(p); вместо этого, не опасаясь поведения undefined в этом случае?
  • Если std::is_trivially_destructible<T>::value имеет значение, выполняется ли пропуск T при *p (т.е. при вызове tPtr->~T();) поведение undefined?
  • Для любого типа U, для которого выполняется std::is_trivially_assignable<T, U>::value, std::memcpy(&t, &u, sizeof(U)); эквивалентно t = std::forward<U>(u); (для любого T типа T и U типа U), или это вызовет undefined поведение?

Ответы

Ответ 1

  • Нет, вы не можете. В этом хранилище нет объекта типа T и доступ к хранилищу, как если бы он был равен undefined. См. Также T.C. answer здесь.

    Просто пояснить формулировку в [basic.life]/1, в которой говорится, что объекты с пустой инициализацией живы из хранилища распределение вперед: эта формулировка, очевидно, относится к инициализации объекта. Нет объекта, инициализация которого является пустой, когда выделяется необработанное хранилище с помощью operator new или malloc, поэтому мы не можем считать его "живым", потому что "он" не существует. Фактически, только объекты, созданные определением с пустой инициализацией, могут быть доступны после того, как было выделено хранилище, но до того, как произойдет пустая инициализация (т.е. Их определение встречается).

  • Опускание вызовов деструктора никогда не приводит к поведению undefined. Однако бессмысленно предпринимать попытки оптимизации в этой области, например. шаблонов, поскольку тривиальный деструктор просто оптимизирован.

  • В настоящее время требование быть тривиально копируемым, и типы должны соответствовать. Однако это может быть слишком строгим. Dos Reis N3751, по крайней мере, предлагает отличные типы для работы, и я мог представить, что это правило распространяется на тривиальное назначение копии по одному типу в будущее.

    Однако то, что вы специально показали, не имеет большого смысла (не в последнюю очередь потому, что вы просите присвоение скалярному значению x, которое плохо сформировано), поскольку тривиальное присвоение может выполняться между типами, назначение которых не фактически "тривиальным", т.е. имеет ту же семантику, что и memcpy. Например. is_trivially_assignable<int&, double> не означает, что его можно "назначить" другому, скопировав представление объекта.

Ответ 2

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

Но в заметке говорится о формальном ограничении, поэтому, вероятно, во многих случаях это безопасно. Не гарантировано.

  1. Нет. is_assignable даже не гарантирует, что назначение будет законным при определенных условиях:

Эта черта не проверяет ничего за пределами непосредственного контекста выражения присваивания: если использование T или U будет запускать специализированные шаблоны, генерировать неявно определенные специальные функции-члены и т.д., а те имеют ошибки, присваивание не может компилироваться, даже если std:: is_assignable:: value компилируется и оценивается как true.

То, что вы описываете, больше похоже на is_trivially_copyable, в котором говорится:

Объекты тривиально-копируемых типов являются единственными объектами С++, которые могут быть безопасно скопированы с помощью std:: memcpy или сериализованы в/из двоичных файлов с помощью std:: ofstream:: write()/std:: ifstream:: чтение().

  1. Я действительно не знаю. Я бы доверял комментариям KerrekSB.