Чи безпечно викликати розміщення нового на "this" для тривіального об'єкта?


20

Я знаю, що це питання вже задавали кілька разів, але я не зміг знайти відповіді на цей конкретний випадок.

Скажімо, у мене є тривіальний клас, який не володіє жодними ресурсами та має порожній деструктор та конструктор за замовчуванням. Він має декілька змінних членів з ініціалізацією в класі; не одна з них не є const.

Я хочу повторно ініціалізувати і об'єкт такого класу без deInitметоду написання від руки. Чи безпечно це робити так?

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

Я не можу побачити з цим жодної проблеми - об’єкт завжди у дійсному стані, thisвсе ще вказує на ту саму адресу; але це C ++, тому я хочу бути впевненим.


2
Чи є члени об'єкта const?
NathanOliver

2
Якщо це дійсно, чи було б це рівнозначно *this = A{};?
Кевін

2
@Amomum *this = A{};означає, this->operator=(A{});тобто створити тимчасовий об'єкт і призначити його *this, замінивши значення всіх членів даних на тимчасові значення. Оскільки це те, що ви хочете і є (на мою думку) більш читабельним, ніж розміщення нового, я б замість цього пішов.
Кевін

1
@ Кевін ой, моє погано, ти маєш рацію. Чим - я думаю, це повинно бути рівним, якщо копія видалена?
Amomum

1
замість пояснення класу словами, набагато краще написати повний клас, а не один метод. дивіться sscce.org
BЈович

Відповіді:


17

Наскільки я знаю, також, як і законності delete this, thisдозволено розміщення нових осіб . Крім того, щодо того this, чи можна потім використовувати інші попередньо вказані вказівники / посилання, є кілька обмежень:

[basic.life]

Якщо після закінчення терміну експлуатації об'єкта і до зберігання, яке займав об'єкт, буде повторно використаний або випущений, у місці зберігання, який займав оригінальний об'єкт, створюється новий об'єкт, вказівник, який вказував на початковий об'єкт, посилання на згаданий вихідний об'єкт, або ім'я оригінального об'єкта автоматично посилатиметься на новий об'єкт і, як тільки починається термін експлуатації нового об'єкта, може бути використаний для маніпулювання новим об'єктом, якщо:

  • сховище для нового об’єкта точно перекриває місце зберігання, яке займав оригінальний об'єкт, і
  • новий об’єкт того ж типу, що і вихідний об'єкт (ігноруючи найвищий рівень cv-кваліфікаторів), та
  • тип вихідного об'єкта не має кваліфікації const, і, якщо тип класу, не містить жодного нестатичного члена даних, тип якого const-кваліфікований або референтний тип, і
  • ні початковий об’єкт, ні новий об'єкт не є потенційно перекриваючим субооб'єктом ([intro.object]).

Перші два задоволені в цьому прикладі, але останні два потрібно взяти до уваги.

Щодо третього пункту, враховуючи те, що функція не відповідає рівню const, слід вважати досить безпечним припустити, що вихідний об'єкт не const. Помилка знаходиться на стороні абонента, якщо неприхотність була відкинута. Що стосується const / reference member, я думаю, що це можна перевірити, стверджуючи, що це можна присвоїти:

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

Звичайно, оскільки присвоєння є вимогою, ви можете замість цього просто використовувати те, *this = {};що я б очікував для створення тієї ж програми. Можливо, більш цікавим випадком використання може бути повторне використання пам’яті *thisдля об’єкта іншого типу (що не відповідає вимогам щодо використання this, принаймні, без переосмислення + відмивання).

Подібне до delete this, розміщення нового місця thisнавряд чи можна охарактеризувати як "безпечне".


Цікаво. Чи можливо переконатися, що всі ці умови задовольняють static_assert для деяких типів ознак? Не впевнений, чи є хтось про членів const ...
Amomum

1
@Amomum Я не думаю, що суббоєкт - це те, що можна перевірити. Члени const або посилання роблять клас неприсвоюваним, що, на мою думку, не може трапитися інакше для тривіального класу.
eerorika

чи означає це, що суворість-псевдонім вступає в силу щодо стійкості? чи можете ви навести приклад, коли це може ввійти в гру?
darune

1
@darune Створіть об’єкт const, розмістіть його на новому, використовуйте оригінальну назву об’єкта (або попередньо існуючий вказівник чи референс), і поведінка буде невизначеною. Насправді так само, як і сувора оптимізація, але якщо не зовсім однакова.
eerorika

1
Зворотним delete ptrє new T(). Зворотним new(ptr)T{}є ptr->~T();. stackoverflow.com/a/8918942/845092
мукають Duck

7

Правила, які стосуються цього, містяться в [basic.life] / 5

Програма може закінчити життя будь-якого об'єкта шляхом повторного використання сховища, яке займає об'єкт, або явного виклику деструктора для об'єкта класового типу. Для об’єкта класового типу програмі не потрібно явно викликати деструктор перед тим, як сховище, яке займає об'єкт, буде повторно використане або звільнено; однак, якщо явного виклику деструктора немає або якщо вираз видалення не використовується для звільнення сховища, деструктор неявно не викликається, і будь-яка програма, що залежить від побічних ефектів, викликаних деструктором, має невизначене поведінку.

і [basic.life] / 8

Якщо після закінчення терміну експлуатації об'єкта і до зберігання, яке займав об'єкт, буде повторно використаний або випущений, у місці зберігання, який займав оригінальний об'єкт, створюється новий об'єкт, вказівник, який вказував на початковий об'єкт, посилання на згаданий вихідний об'єкт, або ім'я оригінального об'єкта автоматично посилатиметься на новий об'єкт і, як тільки починається термін експлуатації нового об'єкта, може бути використаний для маніпулювання новим об'єктом, якщо:

  • сховище для нового об’єкта точно перекриває місце зберігання, яке займав оригінальний об'єкт, і

  • новий об’єкт того ж типу, що і вихідний об'єкт (ігноруючи найвищий рівень cv-кваліфікаторів), та

  • тип вихідного об'єкта не має кваліфікації const, і, якщо тип класу, не містить жодного нестатичного члена даних, тип якого const-кваліфікований або референтний тип, і

  • ні початковий об’єкт, ні новий об'єкт не є потенційно перекриваючим субооб'єктом ([intro.object]).

Оскільки ваш об’єкт є тривіальним, вам не доведеться турбуватися про [basic.life] / 5 і доки ви задовольняєте точки кулі від [basic.life] / 8, то це безпечно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.