Move constructor disappears in derived class when adding custom destructor - c++

I have a move-only Base class and a Derived which inherits Base's constructors. I would like to give a Derived a custom destructor, but when I do so it no longer inherits Base's move constructor. Very mysterious. What is happening?
godbolt
// move-only
struct Base {
Base() = default;
Base(Base const &) = delete;
Base(Base &&) {}
};
struct Derived : public Base {
using Base::Base;
// remove this and it all works
~Derived() { /* ... */ }
};
int main() {
Base b;
// works
Base b2 = std::move(b);
Derived d;
// fails
Derived d2 = std::move(d);
}

The move constructor is not inherited with using Base::Base; in the way that you seem to think it is, because the move constructor in Base does not have the signature that a move constructor in Derived would have. The former takes a Base&&, the latter a Derived&&.
Then in Derived you are declaring a destructor. This inhibits the implicit declaration of a move constructor for Derived. So there is no move constructor in Derived.
The compiler then falls back to Derived's implicitly generated copy constructor for Derived d2 = std::move(d);. But that is defined as deleted because the base class of Derived is not copy-able. (You manually deleted Bases copy constructor.)
In overload resolution the deleted copy constructor is chosen over the Base classes inherited Base(Base&&) constructor (although a Derived rvalue could bind to Base&&), because the latter requires a conversion sequence that is not considered exact match, while binding to a const Derived& is considered exact match for the purpose of overload resolution.
Also there is the proposed wording for the resolution of CWG issue 2356 which would exclude the inherited Base move constructor from participating in overload resolution at all. (From what I can tell this is what the compiler are implementing already.)
If you don't have a good reason to declare a destructor, don't do so. If you do have a reason, then you need to default the move operations again, as you did for the move constructor in Base. (You probably want to default the move assignment operator as well if the classes are supposed to be assignable.)
If you intend to use the class hierarchy polymorphically, you should declare a virtual (defaulted) destructor in the polymorphic base, but you do not need to declare a destructor in the derived classes.

Move constructors are generated under specific circumstances.
https://en.wikipedia.org/wiki/Special_member_functions
In creating a destructor, you have stopped the generation of a move constructor by the compiler.
Also, create a virtual Base destructor if you don't have one. Default it if it doesn't have to do anything special. Same with your Base move constructor, just don't leave it empty, declare it default. You're using =delete, use =default as well.

The inherited move constructor does not have the signature for the derived class.
In the first case without the explicitly declared destructor the compiler implicitly declares the default move constructor for the derived class.
In the second case when the destructor is explicitly declared the move constructor is not implicitly declared by the compiler.
From the C++ 17 Standard (15.8.1 Copy/move constructors)
8 If the definition of a class X does not explicitly declare a move
constructor, a non-explicit one will be implicitly declared as
defaulted if and only if
(8.1) X does not have a user-declared copy constructor,
(8.2) X does not have a user-declared copy assignment operator,
—(8.3) X does not have a user-declared move assignment operator, and
> —(8.4) X does not have a user-declared destructor.
But in any case the ,move constructor of the base class is not the move constructor of the derived class due to different signatures.

Related

Must a c++ interface obey the rule of five?

What is the correct way to declare instantiation methods when defining an interface class?
Abstract base classes are required to have a virtual destructor for obvious reasons. However, the following compilation warning is then given: "'InterfaceClass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move
assignment operator", which is the 'rule of five'.
I understand why the 'rule of five' should be obeyed in general, but is it still applicable for an abstract base class or interface?
My implimentation is then:
class InterfaceClass
{
// == INSTANTIATION ==
protected:
// -- Constructors --
InterfaceClass() = default;
InterfaceClass(const InterfaceClass&) = default;
InterfaceClass(InterfaceClass&&) = default;
public:
// -- Destructors --
virtual ~InterfaceClass() = 0;
// == OPERATORS ==
protected:
// -- Assignment --
InterfaceClass& operator=(const InterfaceClass&) = default;
InterfaceClass& operator=(InterfaceClass&&) = default;
// == METHODS ==
public:
// Some pure interface methods here...
};
// == INSTANTIATION ==
// -- Destructors --
InterfaceClass::~InterfaceClass()
{
}
Is this correct? Should these methods be = delete instead? Is there some way of declaring the destructor to be virtual pure whilst also somehow remaining default?
Even if I declare the destructor as: virtual ~InterfaceClass() = default;, if I do not explicitly default the other four then I will get the same compiler warning.
Tl;dr: What is the correct way to satisfy the 'rule of five' for an interface class as the user must define a virtual destructor.
Thanks for your time and help!
Is this correct? Should these methods be = delete instead?
Your code seems correct. The need of defining special copy/move member functions as default and protected comes clear when you try to copy a derived class polymorphycally. Consider this additional code:
#include <iostream>
class ImplementationClass : public InterfaceClass
{
private:
int data;
public:
ImplementationClass()
{
data=0;
};
ImplementationClass(int p_data)
{
data=p_data;
};
void print()
{
std::cout<<data<<std::endl;
};
};
int main()
{
ImplementationClass A{1};
ImplementationClass B{2};
InterfaceClass *A_p = &A;
InterfaceClass *B_p = &B;
// polymorphic copy
*B_p=*A_p;
B.print();
// regular copy
B=A;
B.print();
return 0;
}
And consider 4 options for defining special copy/move member functions in your InterfaceClass.
copy/move member functions = delete
With special copy/move member functions deleted in your InterfaceClass, you would prevent polymorphic copy:
*B_p = *A_p; // would not compile, copy is deleted in InterfaceClass
This is good, because polymorphic copy would not be able to copy the data member in the derived class.
On the other hand, you would also prevent normal copy, as the compiler won't be able to implicitly generate a copy assignment operator without the base class copy assignment operator:
B = A; // would not compile either, copy assignment is deleted in ImplementationClass
copy/move special member functions public
With copy/move special member functions as default and public, (or without defining copy/move member functions), normal copy would work:
B = A; //will compile and work correctly
but polymorphic copy would be enabled and lead to slicing:
*B_p = *A_p; // will compile but will not copy the extra data members in the derived class.
copy/move special member functions not defined
If move&copy special member functions are not defined, behavior with respect to copy is similar to 2: the compiler will implicitly generate deprecated copy special members (leading to polymorphic slicing). However in this case the compiler will not implicitly generate move special members, so copy will be used where a move would be possible.
protected copy/move member functions (your proposal)
With special copy/move member functions as default and protected, as in your example, you will prevent polymorphic copy which would otherwise had lead to slicing:
*B_p = *A_p; // will not compile, copy is protected in InterfaceClass
However, the compiler will explicitly generate a default copy assignment operator for InterfaceClass, and ImplementationClass will be able to implicitly generate its copy assignment operator:
B = A; //will compile and work correctly
So your approach seems the best and safest alternative
For destructor, if you want to make it both pure virtual and default, you can default it in implementation:
class InterfaceClass
{
// -- Destructors --
virtual ~InterfaceClass() = 0;
};
InterfaceClass::~InterfaceClass() = default;
It does not make much difference if the destructor is default or empty, though.
Now for the rest of your question.
Typically you should have copy constructor and assignment operator defaulted. This way, they don't prevent making default assignment operators and copy constructor in derived classes. Default implementation is correct, as there's no invariant to copy.
So if you want to implement easily Clone method, deleting copy constructor would harm:
class InterfaceClass
{
virtual InterfaceClass* Clone() = 0;
virtual ~InterfaceClass() = 0;
};
class ImplementationClass : public InterfaceClass
{
public:
// This will not work if base copy constructor is deleted
ImplementationClass(const ImplementationClass&) = default;
// Writing copy constructor manually may be cumbersome and hard to maintain,
// if class has a lot of members
virtual ImplementationClass* Clone() override
{
return new ImplementationClass(*this); // Calls copy constructor
}
};
Note also that default implementation of copy/move constructor would not be accidentally used against intention - as instances of abstract base class cannot be created. So you will always be copying derived classes, and they should define, if copying is legal or not.
However, for some classes making copies totally would not make sense, in this case it may be wise to prohibit copying/assigning in the very base class.
Tl;dr: it depend, but most likely you'd better leave them as default.
In general, if any of the big 3 special functions has none-[trivial/default] definition, the other 2 should be defined. If the 2 special move functions have none-[trivial-default] definition, then you need take care of all 5.
In the case of an interface with a nop defined dtor, you don't need bother defining the rest - unless for other reasons.
Even none-trivial definitions do not nessecitate a redefinition of other functions; only when some sort of resource management(e.g. memory, file, io, sync...) is involved, one need define the big 3(5).

Does a default virtual destructor prevent compiler-generated move operations?

Inspired by the post Why does destructor disable generation of implicit move methods?, I was wondering if the same is true for the default virtual destructor, e.g.
class WidgetBase // Base class of all widgets
{
public:
virtual ~WidgetBase() = default;
// ...
};
As the class is intended to be a base class of a widget hierarchy I have to define its destructor virtual to avoid memory leaks and undefined behavior when working with base class pointers. On the other hand I don't want to prevent the compiler from automatically generating move operations.
Does a default virtual destructor prevent compiler-generated move operations?
Yes, declaring any destructor will prevent the implicit-declaration of the move constructor.
N3337 [class.copy]/9: If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared
as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
Declaring the destructor and defining it as default counts as user-declared.
You'll need to declare the move constructor and define it as default yourself:
WidgetBase(WidgetBase&&) = default;
Note that this will in turn define the copy constructor as delete, so you'll need to default that one too:
WidgetBase(const WidgetBase&) = default;
The rules for copy and move assignment operators are pretty similar as well, so you'll have to default them if you want them.
Not a solution, but one of possible workarounds.
You can inherit all of your classes from a class that has only default virtual destructor.
I checked using GCC 9 and Apple's Clang++ with -std=c++17: both of them generate move constructors for classes that inherit the class below.
class Object {
public:
virtual ~Object() = default;
};
The class below will indeed have a move constructor.
class Child : public Object {
public:
Child(std::string data) : data(data) {
}
private:
std::string data;
};
Another possible but risky workaround would be to not declare virtual destructors at all. It would introduce the following risks:
All objects must always be destructed by someone who knows their exact type. Which is not really that big of a problem in a nicely designed C++ code.
When object of such class is stored in a container like std::vector or std::list it must always be wrapped using std::shared_ptr. std::unique_ptr would cause leaks! That's related to their differences related to storing deleter.

Default move constructor in a sub class

In C++11, if the base class has defined its own move (copy) constructor (assignment operator), does its subclass need to define its own move (copy) constructor (assignment operator) in where call the base class's corresponding constructor/operator is called explicitly?
Is it a good idea to define the constructor, destructor, move/copy constructor (assignment operator) clearly every time?
struct Base {
Base() {}
Base(Base&& o);
};
struct Sub : public Base {
Sub(Sub&& o) ; // Need I do it explicitly ? If not,what the compiler will do for me
};
The compiler will generate a default move constructor if you don't specify one in the base class (except some cases, e.g. there's a base class with a deleted move constructor) but you should, in any case, call explicitly the base class' one if you have it:
Sub(Sub&& o) : Base(std::move(o))
According to the standard (N3797) 12.8/9 Copying and moving class objects [class.copy]:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.
As such, if your class meets the above requirements then a default move constructor will be implicitly declared for you.
As already being stated, the base-class has no knowledge of any sub-classes. As such, whether you declare a move constructor in one base class has no effect on the implicit generation of a move constructor in its sub-classes.
As far as it concerns whether you should declare explicitly a constructor/destructor etc. of a class, there's this nice article.
No, you don't have. I'll be automatically generated like default/copy constructor.
From this page,
Implicitly-declared move constructor
If no user-defined move constructors 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 copy assignment operators
there are no user-declared move assignment operators
there are no user-declared destructors
(until C++14) the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section
then the compiler will declare a move constructor as an inline public member of its class with the signature T::T(T&&).
A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default.
Your struct Sub has no user-declared copy constructors, copy assignment operators, move assignment operators or destructors.
And,
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
It is not user-provided (meaning, it is implicitly-defined or defaulted), and if it is defaulted, its signature is the same as implicitly-defined
T has no virtual member functions
T has no virtual base classes
The move constructor selected for every direct base of T is trivial
The move constructor selected for every non-static class type (or array of class type) member of T is trivial
T has no non-static data members of volatile-qualified type
(since C++14)
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.
Implicitly-defined move constructor
If the implicitly-declared move constructor is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler. For union types, the implicitly-defined move constructor copies the object representation (as by std::memmove). For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.
The move constructor of Base is not trivial (it's user-defined). So, the implicitly-defined move constructor of Sub will work as "the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument."

c++ Inheriting private copy constructor: how doesn't this yield a compile time error?

In C++, if we have this class
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
and then we have a derived class
class Dervied: private Uncopyable{
};
My question is: why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ? Won't the generated code try to access base class private members ?
C++11 12.8/7 states "If the class definition does not explicitly declare a copy constructor, one is declared implicitly." so Dervied has an implicitly declared copy constructor. 12.8/11 says:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
a variant member with a non-trivial corresponding constructor and X is a union-like class,
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
for the copy constructor, a non-static data member of rvalue reference type, or
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
Specifically, the third bullet applies: Dervied has a direct base class Uncopyable that cannot be copied because overload resolution results in a function that is inaccessible from Dervied::Dervied(const Dervied&). As a result Dervied's implicitly declared copy constructor is declared as deleted, resulting in a compile time error if and only if that copy constructor is called.
why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ?
Because the compiler generates them only when they are needed by the code being compiled. Write some code using the derived class where the copy constructor and/or assignment operator are involved, and you will see the compile-time error you are looking for.
The private in the inheritance makes them private to Derived, it can still see them, classes that use Derived can't.
The derived class will inherit the private copy constructor but will not need to use it unless you copy an object of derived type, as in this example.
The compiler does not auto-generate constructors/operators unless they are used and no other constructor/operator can be used to do that operation (i.e. a copy operation can be used in some situations where a move operation would suffice). The latter statement results in the following set of rules.
Here are the rules to the auto-generation of certain member functions:
Default constructor (if no other constructor is explicitly declared)
Copy constructor if no move constructor or move assignment operator
is explicitly declared. If a destructor is declared generation of a
copy constructor is deprecated.
Move constructor if no copy
constructor, move assignment operator or destructor is explicitly
declared.
Copy assignment operator if no move constructor or move assignment
operator is explicitly declared. If a destructor is declared
generation of a copy assignment operator is deprecated.
Move assignment operator if no copy constructor, copy assignment operator
or destructor is explicitly declared.
Destructor
The list is taken from this Wikipedia page.
One class cannot call private methods on another class, but it can inherit as much as it is coded too. This code just includes the member functions from Uncopyable in Derived.
Imagine if you wrote a class inheriting from std::vector. You can still erase, insert, push_back and do all those sorts of things. Because these are all public or protected vector member functions, they in turn call implementation specific private functions that do the low level things like manage memory. Your code in this derived class couldn't call those memory management functions directly though. This is used to insure the creators of the vector can change the internal details freely without breaking your use of the class.
If your example is what the code actually looks like, then this it is a common pattern used to make things that cannot be copied. It would make code like the following produce a compiler error:
Derived Foo;
Derived Bar;
Foo = Bar
It would also make the code throw an error on the following:
int GetAnswer(Derived bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
This example fails because a copy of foo is made and passed as the parameter in the function GetAnswer.
There are many reasons why something might not be copyable. The most common I have experienced is that the object manages some low level resource a single file, an opengl context, an audio output, etc... Imagine if you had a class that managed a log file. If it closed the file in the deconstructor, what happens to the original when a copy is destroyed.
Edit: to pass an Uncopyable class to a function, pass it by reference. The Following function does not make a copy:
int GetAnswer(Derived& bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
It would also cause a compiler error if all the constructor were private and the class was instantiated. But even if all the member function even constructors were private and the class was never instantiated that would be valid compiling code.
Edit: The reason a class with constructor is that there maybe other way to construct it or it maybe have static member functions, or class functions.
Sometimes factories are used to build object which have no obvious constructor. These might have functions to whatever magic is required to make the umakeable class instance. The most common I have seen is just that there was another constructor that was public, but not in an obvious place. I have also seen factories as friend classes to the unconstructable class so they could call the constructors and I have seen code manually twiddle bits of memory and cast pointers to the memory it to an instance of a class. All of these patterns are used to insure that a class is correctly created beyond just the guarantees C++ supplies.
A more common pattern I have seen is static member functions in classes.
class uncreateable
{
uncreateable() {}
public:
static int GetImportantAnswer();
};
Looking at this it can be seen that not only do I not need to create a instance of the class to call GetImportantAnswer() but I couldn't create an instance if I wanted. I could call this code using the following:
int answer;
answer = uncreateable::GetImportantAnswer();
Edit: Spelling and grammar
Well, actually this program does not compile with g++:
#include <iostream>
using namespace std;
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
};
class Derived: private Uncopyable{
};
int main() {
Derived a;
Derived b = a;
}
compiler output:
$ g++ 23183322.cpp
23183322.cpp:10:88: warning: control reaches end of non-void function [-Wreturn-type]
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
^
23183322.cpp:13:7: error: base class 'Uncopyable' has private copy constructor
class Derived: private Uncopyable{
^
23183322.cpp:9:5: note: declared private here
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
^
23183322.cpp:19:15: note: implicit copy constructor for 'Derived' first required here
Derived b = a;
^
1 warning and 1 error generated.

Subclassing, assignment operator and copy constructor

I have a bunch of questions related to this, I couldn't find exact answers.
Class A is the main class, and B is a subclass
If A::operator=(const A & other) was defined, does the default implementation of B::operator= copies members of B then calls A::operator= ?
If A copy constructor was defined, does the default implementation of the copy constructor for B copy-constructs members of B, then calls A copy constructor?
Do I need to define above functions virtual in A to get this behavior? (I suppose yes for operator= and no for the copy constructor, as virtual constructors is a nonsense?)
Can I forbid overloading the assignment operator or the copy constructor for subclasses of A, to force the use of default implementations?
The idea behind this, being to offer a plugin API for my users. I need the API to be C++ as scripts are too slow (I'll try JIT compilation one day), but it should be very simple.
If the default copy-assignment operator of class B is not deleted, [class.copy]/28
The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy-/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list [i.e. in the order in which they're listed after class X : /*here*/ {/*...*/};], and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition.
Similarly, [class.copy]/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.
The order is: first the base class(es) (base class subobjects), then the direct non-static data members, similarly to 1).
For the behaviour described in 1) and 2), no. Virtual assignment operators are hardly ever useful. Constructors may not be virtual at all (doesn't make sense).
In order for a virtual function in a derived class B to override a virtual function in a base class A, it must have the same parameter types. That is, you could have a virtual A& operator=(A const&); in the base class A, but the override in class B had to look like virtual B& operator=(A const&);, which is not a copy-assignment operator for B, because of the parameter type.
Not without "hacks". And you're not actually overloading it, but hiding all base class assignment operators. Otherwise, this would be legal:
class A {};
class B { int i; };
A a;
B b = a; // using A::operator=(A const&)