Why does defaulted (user declared) destructor in Base1
prevent generation of move constructor/operator in Child1
class, but everything work fine when I move member data
from Base (Base2
) to Child (Child2
) class?
struct Data {
Data() {}
Data(Data&&) noexcept { cout << "Move constructor" << endl; }
Data& operator=(Data&&) noexcept {
cout << "Move assign" << endl;
return *this;
}
vector<int> vec;
};
struct Base1 {
virtual void fun() { cout << "Base1::fun" << endl; }
virtual ~Base1() = default;
Data data;
};
struct Child1 : public Base1 {
void fun() override { cout << "Child1::fun" << endl; }
};
struct Base2 {
virtual void fun() { cout << "Base2::fun" << endl; }
virtual ~Base2() = default;
};
struct Child2 : public Base2 {
void fun() override { cout << "Child2::fun" << endl; }
Data data;
};
int main() {
Child1 c1;
auto obj1 = std::move(c1); // error
Child2 c2;
auto obj2 = std::move(c2);
}
My current understanding is that when I declare destructor as “default
” in Base (BaseDel
), then move constructor should be “deleted
” in Base (BaseDel
) and in Child (ChildDel
) class. Is this correct? Member location shouldn’t matter, I think. If I do that explicit, I get expected error:
struct BaseDel {
BaseDel() {}
virtual void fun() { cout << "BaseDel::fun" << endl; }
BaseDel(BaseDel&& st) = delete;
virtual ~BaseDel() = default;
};
struct ChildDel : public BaseDel {
ChildDel() {}
void fun() override { cout << "ChildDel::fun" << endl; }
Data data;
};
int main() {
ChildDel cd;
auto objd = std::move(cd); // OK, expected error
}
The implicit move constructor is not (only) deleted, it is not declared in the first place when you have a user-declared destructor, as is the case with Base1
and Base2
.
Therefore the move constructor can never be considered in overload resolution and so auto obj1 = std::move(c1);
, while it can call Child1
's move constructor, needs to fall back to copy construction for the Base1
subobject.
The implicitly-declared copy constructors of both Base1
and Child1
are defined as deleted, because Data
's implicitly-declared copy constructor is defined as deleted, because Data
has a user-defined move constructor. Therefore auto obj1 = std::move(c1);
will fail with an error that the implicitly-declared copy constructor is deleted.
For Base2
the copy constructor is not defined as deleted, because it doesn't have a Data
member and so auto obj2 = std::move(c2);
will call Child2
's move constructor (which also uses Data
's move constructor), but use the copy constructor for the Base2
subobject.