Уничтожьте и затем создайте новый объект, используя одну и ту же переменную
Иногда приятно начинать заново. В С++ я могу использовать следующий простой маневр:
{
T x(31, Blue, false);
x.~T(); // enough with the old x
::new (&x) T(22, Brown, true); // in with the new!
// ...
}
В конце области действия деструктор снова запустится, и все будет хорошо. (Пусть также говорят, что T
немного особенный и ему не нравится назначаться, не говоря уже об обмене.) Но что-то говорит мне, что это не всегда без риска уничтожить все и повторить попытку. Существует ли возможный улов с этим подходом?
Ответы
Ответ 1
Я думаю, что единственный способ сделать это действительно безопасным для использования - потребовать, чтобы вызываемый конструктор был noexcept
, например, добавив static_assert
:
static_assert(noexcept(T(22, Brown, true)), "The constructor must be noexcept for inplace reconstruction");
T x(31, Blue, false);
x.~T();
::new (&x) T(22, Brown, true);
Конечно, это будет работать только для С++ 11.
Ответ 2
Если конструктор T
выбрасывает вторую конструкцию, возникает проблема. Если вам нравятся подходы грубой силы, проверьте это:
T x(31, Blue, false);
x.~T();
const volatile bool _ = true;
for(;_;){
try{
::new (&x) T(22, Brown, true);
break; // finally!
}catch(...){
continue; // until it works, dammit!
}
}
Он даже обеспечивает надежную гарантию!
На более серьезной ноте, это похоже на наступление на наземную мишень, зная, что она исчезнет, если вы переместите ногу...
И здесь существует способ преодоления undefined поведения двойного разрушения:
#include <cstdlib>
T x(31, Blue, false);
x.~T();
try{
::new (&x) T(22, Brown, true);
}catch(...){
std::exit(1); // doesn't call destructors of automatic objects
}
Ответ 3
Если выражается выражение конструкции T, вы удваиваете уничтожение объекта, который является UB. Конечно, даже желание сделать это свидетельствует о неудаче проектирования.
Ответ 4
Я попытался скомпилировать его, но я только осмелился запустить его под отладчиком. Поэтому я взглянул на демонтаж моего старого компилятора (комментарии тоже являются компиляторами):
@1 sub nerve.cells, fa0h
@2 xor x, x // bitch.
@3 mov out, x
@4 test out, out
@5 jne @1
@6 xor x, x // just in case.
@7 sub money, 2BC // dammit.
@8 mov %x, new.one
@8 cmp new.one, %x
@9 jne @7
...
@25 jmp @1 // sigh...
Ответ 5
Ммм. Поскольку вы делаете все, что С++ обескураживает, я думаю, что все забывают о goto.
Обратите внимание, что после явного вызова X.~T()
и до того, как он будет восстановлен 1 все равно будет двойное разрушение, если кто-то сделал goto
до объявления/инициализации переменной x (даже внутри внутреннего блока видимости).
Поскольку вы, очевидно, можете просто документировать это, я не буду переживать, когда пытаюсь "исправить" это. Вы могли бы концептуально разработать класс RAII для управления реконструкцией объекта на месте, делая этот маневр безопасным для goto в любом месте. Обратите внимание, что вы могли бы получить, что вызов конструктора-компоновщика-места прекрасно переадресовывается из деструктора объекта менеджера RAII. Жизнь хороша.
Другие оговорки по-прежнему применяются, конечно (см. другие ответы)
1 мы можем предположить, что для этого момента не может быть построено
Ответ 6
Нет ничего, чтобы остановить вас, это будет работать в большинстве случаев. Но, как и в случае с большим количеством С++, знание особенностей ваших дел будет заключаться в различии между работой, которую вы хотите, и дампом ядра. Есть очень немного примеров причин, по которым я мог понять, почему вы хотели бы сделать это в реальной программе, единственной, которая имеет смысл, является файл с отображением памяти.