Перемещаются - из объектов, требуемых для уничтожения?

Если я перемещаю-конструкцию a из b, все же необходимо уничтожить b, или я могу уйти без этого?

Этот вопрос перешел мне в голову при реализации шаблона optional<T>. Выдержки:

~optional()
{
    if (initialized)
    {
        reinterpret_cast<T*>(data)->~T();
    }
}

optional(optional&& o) : initialized(o.initialized)
{
    if (initialized)
    {
        new(data) T(std::move(*o));   // move from o.data
        o.initialized = false;        // o.data won't be destructed anymore!
    }
}

Конечно, я мог бы просто заменить bool initialized трехзначным перечислением, которое различает инициализированный, неинициализированный и перемещенный. Я просто хочу знать, нужно ли это строго.

Ответ 1

Да, по-прежнему необходимо уничтожить b. Перемещенный из объекта - действительный, построенный объект. В некоторых случаях он может даже содержать ресурсы, которые по-прежнему необходимо утилизировать. В общем коде, который вы показываете, T может даже не иметь конструктор перемещения. В этом случае вы можете ссылаться на конструктор копирования. Таким образом, вы можете определенно не предполагать, что ~ T() является no-op и может быть отменено.

Ответ 2

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

В вашем примере проще всего не инициализировать false в перемещенном объекте. Значение по-прежнему определено в правильном состоянии после перемещения из него, а деструктор значения r, о котором вы говорите, очистит его без дальнейшего вмешательства.

Ответ 3

Я бы хотел ответить "Нет" на ваш вопрос, но я не уверен, что это даже правильный вопрос. Рассмотрим следующее:

{  // start of scope
    T maybe_moved;
    if(some_condition) {
        T(std::move(maybe_moved));
    }
// end of scope
}

T::~T(), очевидно, должен быть вызван только один раз для объекта maybe_moved. Если конструктор перемещения вызовет его, как бы вы сделали такой безобидный код, работающий?