Уничтожить, а затем построить новый объект, используя ту же переменную - PullRequest
28 голосов
/ 12 января 2012

Иногда приятно начать все сначала. В C ++ я могу использовать следующий простой маневр:

{

    T x(31, Blue, false);

    x.~T();                        // enough with the old x

    ::new (&x) T(22, Brown, true); // in with the new!

    // ...
}

В конце области действия деструктор снова запустится, и все будет хорошо. (Допустим также, что T немного особенный и не любит, когда его назначают, не говоря уже об обмене.) Но что-то подсказывает мне, что не всегда без риска все разрушить и попробовать снова. Есть ли возможный улов при таком подходе?

Ответы [ 6 ]

28 голосов
/ 12 января 2012

Я думаю, что единственный способ сделать это действительно безопасным в использовании - это потребовать, чтобы вызываемый конструктор был 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);

Конечно, это будет работать только для C ++ 11.

17 голосов
/ 12 января 2012

Если конструктор 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!
  }
}

Он даже обеспечивает гарантию исключений!


На более серьезной ноте это все равно, что наступить на мину, зная, что она сработает, если вы пошевелите ногой ...

И на самом деле - это способ обойти неопределенное поведение двойного уничтожения здесь:

#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
}
9 голосов
/ 12 января 2012

Если сработает выражение конструкции T, вы дважды уничтожите объект, который является UBКонечно, даже желание сделать это свидетельствует о неудаче проекта.

7 голосов
/ 12 января 2012

Я пытался скомпилировать его, но я только осмелился запустить его под отладчиком. Поэтому я посмотрел на разборку, сгенерированную моим старым компилятором (комментарии тоже для компилятора):

@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... 
2 голосов
/ 12 января 2012

Ммм. Поскольку вы делаете все, что не нравится C ++, я думаю, что все забывают о goto .

Обратите внимание, что после явного вызова X.~T() и до его восстановления 1 все равно произойдет двойное уничтожение, если кто-то сделает goto до объявления / инициализации переменной x ( даже внутри блока области видимости ).

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

Другие предостережения все еще применяются, конечно (см. Другие ответы)


1 мы можем предположить, что на этот момент мы не построим

0 голосов
/ 12 января 2012

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

...