質問

In The C++ programming language Edition 4 there is an example of a vector implementation, see relevant code at the end of the message.

uninitialized_move() initializes new T objects into the new memory area by moving them from the old memory area. Then it calls the destructor on the original T object, the moved-from object. Why is the destructor call necessary in this case?

Here is my incomplete understanding: moving an object means that the ownership of the resources owned by the moved-from object are transferred to the moved-to object. The remainings in the moved-from object are some possible members of built-in types that do not need to be destroyed, they will be deallocated when the the vector_base b goes out of scope (inside reserve(), after the swap() call). All pointers in the moved-from object are to be put to nullptr or some mechanism is employed to drop ownership of moved-from object on those resources so that we are safe, then why call the destructor on a depleted object when the "vector_base b" destructor will anyway deallocate the memory after the swap is done?

I understand the need to explicitly call the destructor in the cases when it must be called because we have something to destruct (e.g. drop elements) but I fail to see its meaning after the std::move + deallocation of vector_base. I read some texts on the net and I'm seeing the destructor call of the moved-from object as an signal (to whom or what?) that the lifetime of the object is over.

Please clarify to me what meaningful work remains to be done by the destructor? Thank you!

The code snippet below is from here 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;       
}
役に立ちましたか?

解決

Moving from an object just means that the moved-from object might donate its guts to live on in another live object shortly before it is [probably] going to die. Note, however, that just because an object donated its guts that the object isn't dead! In fact, it may be revived by another donating object and live on that object's guts.

Also, it is important to understand that move construction or move assignment can actually be copies! In fact, they will be copies if the type being moved happens to be a pre-C++11 type with a copy constructor or a copy assignment. Even if a class has a move constructor or a move assignment it may choose that it can't move its guts to the new object, e.g., because the allocators mismatch.

In any case, a moved from object may still have resources or need to record statistics or whatever. To get rid of the object it needs to be destroyed. Depending on the class's contracts it may even have a defined state after being moved from and could be put to new use without any further ado.

他のヒント

In his proposal to implement move semantics through rvalue references, Howard Hinant considered the push for Destructive Move too. But as he pointed in the same article, a move operation basically deals with to objects (source an destination) simultaneously in transition state - even in single threaded applications. The problem with destructive move is that either source, or destination will experience a state in where derived subpart of an object is constructed while base subpart is destroyed or uninitialized. This was not possible prior to move proposal, and is not acceptable yet. So the other choice left was to leave the moved from object in a valid empty state and let the destructor remove the valid empty state. For many practical purposes, destroying valid empty state is a noop; But it's not possible to be generalized for all cases. So, the moved from object is supposed to be either destructed, or assigned(move or copy) to.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top