Почему вызов деструктора после того, как требуется std:: move?

В языке программирования С++ Edition 4 приведен пример реализации вектора, см. соответствующий код в конце сообщения.

uninitialized_move() инициализирует новые объекты T в новую область памяти, перемещая их из старой области памяти. Затем он вызывает деструктор на исходном объекте T, перемещенном объекте. Почему в этом случае необходим вызов деструктора?

Вот мое неполное понимание: перемещение объекта означает, что право собственности на ресурсы, принадлежащие перемещенному объекту, передается перемещенному объекту. Остатки в перемещенном объекте являются некоторыми возможными элементами встроенных типов, которые не нужно уничтожать, они будут освобождены, когда вектор_база b выходит из области видимости (внутри reserve(), после вызова swap()). Все указатели в перемещенном объекте должны быть помещены в nullptr или используется какой-либо механизм для отказа от права собственности на перемещенный объект из этих ресурсов, чтобы мы были в безопасности, а затем зачем вызывать деструктор на обедненном объекте, когда "vector_base b" destructor все равно освободит память после завершения обмена?

Я понимаю необходимость явного вызова деструктора в случаях, когда он должен быть вызван, потому что у нас есть что-то разрушить (например, элементы drop), но я не вижу его значения после std:: move + deallocation vector_base. Я прочитал несколько текстов в сети, и я вижу деструкторный вызов перемещенного объекта как сигнал (кому или что?), Что срок жизни объекта завершен.

Прошу пояснить мне, какая значимая работа еще предстоит сделать деструктору? Спасибо!

Ниже приведен фрагмент кода http://www.stroustrup.com/4th_printing3.html

template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
    if (newalloc<=capacity()) return;                   // never decrease allocation
    vector_base<T,A> b {vb.alloc,size(),newalloc-size()};   // get new space
    uninitialized_move(vb.elem,vb.elem+size(),b.elem);  // move elements
    swap(vb,b);                                 // install new base 
} // implicitly release old space

template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
    using T = Value_type<Out>;      // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
    for (; b!=e; ++b,++oo) {
        new(static_cast<void*>(&*oo)) T{move(*b)};  // move construct
        b->~T();                                // destroy
    }
    return oo;       
}

Ответ 1

Перемещение с объекта означает, что перемещенный объект может пожертвовать свои кишки, чтобы жить в другом живом объекте незадолго до того, как он [вероятно] умрет. Обратите внимание, однако, что только потому, что объект пожертвовал свои кишки, что объект не мертв! Фактически, это может быть возрождено другим жертвообразующим объектом и жить на этом объекте.

Кроме того, важно понимать, что конструкция перемещения или назначение перемещения могут быть действительно копиями! Фактически, они будут копиями, если перемещаемый тип является типом pre-С++ 11 с конструктором копирования или назначением копии. Даже если класс имеет конструктор перемещения или назначение перемещения, он может выбрать, что он не может перемещать свои кишки к новому объекту, например, потому что распределители несовместимы.

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