A user-declared dtor prevents the autogeneration of the move-ctor/-assignment-operator, but will the autogeneration only be prevented in the class where the dtor has been defined or will the autogeneration be prevented in all derived classes too? I am asking this, because I am using many pure virtual classes which all provide a user-declared dtor. Do I have to upgrade now all this classes to get move-support or will it work still out-of-the-box?
Here is an example of how my scenarios currently looks like:
struct BigData {};
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
};
Now, which of the following variants I have to use to be sure, that moving will be used like in the following example?
A a;
std::vector< A > va;
va.push_back( std::move( a ) ); //Should really use move instead of copy
Variant 1: Upgrade base class only
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
BaseA() = default;
BaseA(BaseA&&) = default;
BaseA& operator=(BaseA&&) = default;
BaseA(const BaseA&) = default;
BaseA& operator=(const BaseA&) = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
};
Variant 2: Upgrade derived class only
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
A(const A&) = default;
A& operator=(const A&) = default;
};
Variant 3: Upgrade base class and derived class
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
BaseA() = default;
BaseA(BaseA&&) = default;
BaseA& operator=(BaseA&&) = default;
BaseA(const BaseA&) = default;
BaseA& operator=(const BaseA&) = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
A(const A&) = default;
A& operator=(const A&) = default;
};
Variant 4: Nothing to do
Not having a move constructor in the base vs. having a deleted move constructor in the base are two different things.
For the first, the derived classes can still go with the rule of zero and have a default generated move constructor created by the compiler.
For the latter, the default move would be also deleted in the derived.
When you have a user defined destructor in your class (or a user defined copy constructor, or a user defined copy assignment operator) the default move operations are not provided, but they are not implicitly deleted. Thus the derived class is still entitled for the default move operations if it follows the rules for having them, without the need to explicitly declare them as =default
.
Cpp Reference says:
The implicitly-declared or defaulted move constructor for class
T
is defined as deleted if any of the following is true:...
T
has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); ...
Note that having a user declared destructor doesn't make a class un-moveable, it just doesn't have a default generated move.