Search code examples
c++c++11movemove-semantics

What is the common idiom(s) for resetting the moved object?


In C++ move constructor is required to reset the moved object which to me seems to be a duplication of what the destructor does in most of the cases.

Is it correct that defining a reset-method and using it in both destructor and move-constructor is the best approach? Or maybe there are better ways?


Solution

  • Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.) rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For some types, such as std::unique_ptr, the moved-from state is fully specified.

    "Stolen" resources should not be released as this will usually lead to errors. For example, a move constructor which "steals" a pointer has to ensure that the destructor of the moved-from object won't delete the pointer. Otherwise, there will be a double-free. A common way of implementing this is to reset the moved-from pointer to nullptr.

    Here is an example:

    struct Pointer {
        int *ptr;
    
        // obtain a ptr resource which we will manage
        Pointer(int* ptr) : ptr{ptr} {}
    
        // steal another object's ptr resource, assign it to nullptr
        Pointer(Pointer &&moveOf) : ptr{moveOf.ptr} {
            moveOf.ptr = nullptr;
        }
    
        // make sure that we don't delete a stolen ptr
        ~Pointer() {
            if (ptr != nullptr) {
                delete ptr;
            }
        }
    };
    

    Is it correct that defining a reset-method and using it in both destructor and move-constructor is the best approach? Or maybe there are better ways?

    This depends on the resource which is managed, but typically the destructor and move-constructor do different things. The move constructor steals the resource, the destructor frees a resource if it hasn't been stolen.

    In C++ move constructor is required to reset the moved object which to me seems to be a duplication of what the destructor does in most of the cases.

    You are right that there often is duplication of work. This is because C++ does not have destructive move semantics, so the destructor still gets called separately, even when an object has been moved from. In the example I have shown, ~Pointer() still needs to get called, even after a move. This comes with the runtime cost of checking whether ptr == nullptr. An example of a language with destructive move semantics would be Rust.


    Related Posts: