C++11 – Defaulted Destructor Disables Default Move Constructor

c++c++11defaultdestructormove-semantics

I have run into a problem that a move constructor of a superclass did not get invoked properly when its subclass has an explicitly defaulted destructor. The move constructor does get invoked when the destructor is implicitly defaulted (not provided at all in the supclass definition).

I am aware of the constraints that the compilers should apply to default move constructors. Yet, I have been by all means sure that the compiler should not discriminate between explicitly/implicitly defaulted destructors (or constructors as well) when applying these rules. In other words, explicitly defaulted destructor should not be treated as user-defined one (in contrast to an empty user-defined destructor ).

Tested with MSVC 2019 only.

Am I or MSVC right here?


#include <iostream>

class A {
public:
    A() = default;
    A(const A&) { std::cout << "Auch, they'r making copy of me(?!)" << std::endl; }
    A(A&&) { std::cout << "I am moving :)" << std::endl; }

    ~A() = default;
};

class B : public A {
public:

};

class C : public A {
public:

    C() = default;

};

class D : public A {
public:

    ~D() = default;

};

class E : public A {
public:

    E() = default;
    E(const E&) = default;
    E(E&&) = default;
    ~E() = default;

};

int main()
{

    std::cout << "\n---- A ----\n" << std::endl;

    A a;
    A a2(std::move(a));

    std::cout << "\n---- B ----\n" << std::endl;

    B b;
    B b2(std::move(b));

    std::cout << "\n---- C ----\n" << std::endl;

    C c;
    C c2(std::move(c));

    std::cout << "\n---- D ----\n" << std::endl;

    D d;
    D d2(std::move(d));

    std::cout << "\n---- E ----\n" << std::endl;

    E e;
    E e2(std::move(e));

}


EXPECTED: Display "I am moving :)" in all cases

ACTUAL : Displays "Auch, they'r making copy of me(?!)" in case D

Best Answer

When you declare a defaulted destructor in D, you disable the compiler-generated move constructor and move assignment operator in D (!), not the base class version. This is why you get the expected output with E, where you override the defaulted operation by the compiler by explicitly = defaulting the special member functions. The compiler-generated move constructor does the right thing for movable base class types, so follow the rule of five and = default the special member functions for D.

Have a look at the table in this answer. It's a very useful reference to be kept under the pillow.

Related Question