Безопасно ли называть размещение новым для this для тривиального объекта? - PullRequest
20 голосов
/ 29 октября 2019

Я знаю, что этот вопрос задавался уже несколько раз, но я не смог найти ответ для этого конкретного случая.

Допустим, у меня есть тривиальный класс, который не владеет никакими ресурсами и имеет пустой деструктори конструктор по умолчанию. У него есть несколько переменных-членов с инициализацией в классе;ни один из них не является const.

Я хочу повторно инициализировать объект такого класса без написания метода deInit вручную. Безопасно ли делать это так?

void A::deInit()
{
  new (this)A{};
}

Я не вижу никаких проблем с ним - объект всегда находится в допустимом состоянии, this все еще указывает на тот же адрес;но это C ++, поэтому я хочу быть уверен.

Ответы [ 2 ]

17 голосов
/ 29 октября 2019

Аналогично законности delete this, насколько я знаю, допускается размещение нового для this. Кроме того, относительно того, можно ли впоследствии использовать this или другие ранее существующие указатели / ссылки, существует несколько ограничений:

[basic.life]

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

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
  • новый объект того же типа, что и исходный объект (игнорируя квалификаторы cv верхнего уровня), и
  • тип исходного объекта не является константным и, если тип класса, не содержитлюбой нестатический элемент данных, тип которого является константным или ссылочным типом, и
  • ни исходный объект, ни новый объект не являются потенциально перекрывающимися подобъектами ([intro.object]).

Первые два удовлетворены в этом примере, но последние два необходимо будет принять во внимание.

Относительно третьего пункта, учитывая, что функция не квалифицирована как const,должно быть довольно безопасно предположить, что исходный объект не является константным. Ошибка на стороне вызывающего абонента, если константность была отброшена. Что касается элемента const / reference, я думаю, что это можно проверить, утверждая, что это присваивается:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

Конечно, поскольку присваиваемость является требованием, вы могли бы вместо этого просто использовать *this = {};, что я ожидаюпроизвести ту же программу. Возможно, более интересный вариант использования мог бы заключаться в повторном использовании памяти *this для объекта другого типа (что не соответствовало бы требованиям для использования this, по крайней мере без переосмысления + отмывания).

По аналогии с delete this, новое для this размещение вряд ли можно назвать «безопасным».

7 голосов
/ 29 октября 2019

Правила, которые охватывают это, приведены в [basic.life] / 5

Программа может завершить время жизни любого объекта, повторно используя хранилище, которое занимает объект, илиявно вызывая деструктор для объекта типа класса. Для объекта типа класса программе не требуется явно вызывать деструктор до повторного использования или освобождения хранилища, которое занимает объект;однако, если нет явного вызова деструктора или если выражение-выражение не используется для освобождения хранилища, деструктор не вызывается неявно, и любая программа, зависящая от побочных эффектов, создаваемых деструктором, имеет неопределенное поведение.

и [basic.life] / 8

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

  • хранилище для нового объекта точно перекрывает место хранения, которое исходный объектзанят, и

  • новый объект того же типа, что иисходный объект (без учета cv-квалификаторов верхнего уровня) и

  • тип исходного объекта не является const-квалифицированным, и, если тип класса, не содержит никакихнестатический элемент данных, тип которого является константным или ссылочным, и

  • ни исходный объект, ни новый объект не являются потенциально перекрывающимися подобъектами ([intro.object]).

Поскольку ваш объект тривиален, вам не нужно беспокоиться о [basic.life] / 5 и до тех пор, пока вы удовлетворяете пунктам пули из [basic.life] / 8, тогда это безопасно.

...