0

I have the following program where I test the order of constructor and destructor calls and I was surprised by the output.

#include <iostream>
#include <utility>


class Doctor {
    public:
    Doctor(){
        std::cout << "Doctor Creation\n";
    }

    ~Doctor(){
        std::cout << "Doctor DeCreation\n";
    }
};


class Nurse{
    public:
    int op = 0;
    Nurse(){
        std::cout << "Nurse Creation\n";
    }

    ~Nurse(){
        std::cout << "Nurse " <<  op << " DeCreation\n";
    }
};

class Hospital{
    private:
        Doctor doc;
        Nurse nurse;
    public:
    Hospital(){
        std::cout << "Hospital Creation\n";
    }
    ~Hospital(){
        std::cout << "Hospital DeCreation\n";
    }

    void operate(){
        std::cout << "Hospital Operation\n";
        nurse.op++;
        std::cout << &this->nurse << " " << nurse.op << "\n";
        reset_hospital();
        std::cout << &this->nurse << " " << nurse.op << "\n";
    }

    void reset_hospital(){
        std::cout << &this->nurse << " " << nurse.op << "\n";
        nurse = std::move(Nurse());
        doc = std::move(Doctor());
    }
};

int main(){
    Hospital hospital;
    hospital.operate();
}

https://godbolt.org/z/bf8KbqsTK

The output is:

Doctor Creation
Nurse Creation
Hospital Creation
Hospital Operation
0x7fff2316866c 1
0x7fff2316866c 1
Nurse Creation
Nurse 0 DeCreation
Doctor Creation
Doctor DeCreation
0x7fff2316866c 0
Hospital DeCreation
Nurse 0 DeCreation
Doctor DeCreation

Shouldn't it be Nurse 1 DeCreation on the std::move operation? The current object is destroyed and replaced by the new one? Or for some reason, is it copying instead of moving?

I'm so confused.

  • C++ should be learnt using a [good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) instead of by trial and error. In particular, this is explained in any beginner c++ book. It is not clear what you're confused about. Also, refer to [std::move doesn't actually move anything](https://stackoverflow.com/a/27026280/12002570). – Jason Sep 07 '22 at 02:59
  • In `nurse = std::move(Nurse());`the `std::move` does nothing. `Nurse()` is already an rvalue – doug Sep 07 '22 at 03:01
  • There's no reason why copying (assignment) a new nurse object to an old one via assignment would call the dtor. It just copies the guts (ie:op) of the new one to the old one. – doug Sep 07 '22 at 03:16
  • @doug I believe OP expects the copy assignment to actually be a move assignment, and expects that move assignment to perform a swap. – JohnFilleau Sep 07 '22 at 03:18
  • @JohnFilleau Yeah, I think you're right. It doesn't have a move assigment and if it did it wouldn't normally swap op. I suppose he could write a move assignment with a swap. That should do what he expects. – doug Sep 07 '22 at 03:23

1 Answers1

1

Answer

Nurse 0 DeCreation is shown instead of Nurse 1 DeCreation because:

  1. Nurse has no move assignment operator (see explanation below),
  2. Nurse has a copy assignment operator that is used, and
  3. If Nurse had an implicitly-defined move assignment operator, it would be trivial, and the members would be acted on as if by std::memmove, which would not swap the underlying op member

Move semantics do not guarantee that a swap will occur. In the case of trivially movable member variables, assignment is much more efficient than swapping. Move semantics don't necessarily guarantee that the "moved-from" object will be the same as the previous state of the "moved-to" object. Move semantics only (should) guarantee that the moved-from object is in a valid state. It can be unspecified.

Why no move assignment operator?

https://en.cppreference.com/w/cpp/language/move_assignment

If no user-defined move assignment operators are provided for a class type (struct, class, or union), and all of the following is true:

  • there are no user-declared copy constructors;
  • there are no user-declared move constructors;
  • there are no user-declared copy assignment operators;
  • there is no user-declared destructor,

then the compiler will declare a move assignment operator as an inline public member of its class with the signature T& T::operator=(T&&).

Nurse has a user-declared destructor, and therefore has no implicitly declared move assignment operator

Nurse does have an implicitly defined copy assignment operator, and its members are copied. The first-destroyed Nurse object has op = 0, so you see Nurse 0 DecCreation.

What if it did have a move assignment operator?

If ~Nurse() was not user provided, then Nurse would have an implicitly declared move assignment operator. It would also be a trivial move assignment operator, which only guarantees the members are assigned as if using std::memmove. I.e., byte-wise copied.

JohnFilleau
  • 4,045
  • 1
  • 15
  • 22