C++11 virtual destructors and auto generation of move special functions - c++

The rules for auto generating special move functions (constructor and assignment operator) in C++11 specify that no destructor can be declared. The logic is presumably that, if you need to do something special in destruction, that a move may not be safe.
However, for proper destructor calls in polymorphism, it is necessary to declare a base classes' destructor as virtual (otherwise deleting an instance of a sub class through a pointer of its base class will not properly chain the destructor).
I'm assuming, then, that even an empty destructor would prevent the compiler from automatically generating a special move functions. As in:
class Base {
virtual ~Base() { }
};
You can, however, default the destructor, as in:
class Base {
virtual ~Base() = default;
}
So question 1: Will this allow the compiler to auto generate special move functions?
There is a problem with the explicit default destructor, however. In at least the case of GCC 4.8.2, the signature is implicitly changed to noexcept. As in:
class Base {
virtual ~Base() = default; // compiler changes to:
// virtual ~Base() noexcept;
}
While I have no problem with noexcept in a destructor, this would break the following "client" code:
class Sub : public Base {
virtual ~Sub(); // this declaration is now "looser" because of no noexcept
}
So question 2 is more to the point: is there a way to allow auto generation of special move functions in C++11 and allow proper destructor chaining to sub classes (as described above), all without breaking subclass ("client") code?

No, a defaulted destructor is still considered user defined, so it will prevent the generation of move operations. Also declare the move operations default-ed to make the compiler generate them.
You need to only declare the move operations as default-ed in the base class. In the derived class, the destructor won't be user defined anymore (unless you explicitly say so), so the move operations won't be deleted.
So what I'd do is the following:
class Base
{
virtual ~Base() = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
// probably need to think about copy operations also, as the move disables them
Base(const Base&) = default;
Base& operator=(const Base&) = default;
};
I highly recommend this talk by the person who contributed probably the most to the move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
Or, if you can get your hands on, you should read the Item 17: Understand special member function generation from Scott Meyers' excellent book Effective Modern C++. This issue is excellently explained.
PS: I think you should think a bit more about your base classes. Most of the time, you should use abstract classes, so there will be no need to copy/move instances of them.
PSS: I think by default destructors are marked noexcept in C++11/14, so not explicitly specifying it shouldn't cause any problems:
Inheriting constructors and the implicitly-declared default
constructors, copy constructors, move constructors, destructors,
copy-assignment operators, move-assignment operators are all
noexcept(true) by default, unless they are required to call a function
that is noexcept(false), in which case these functions are
noexcept(false).

Related

C++ Primer 5th Ed - Stanley : Can a base class with empty destructor making it virtual?

I am reading the below excerpts from the above book (chapter 15 - Object Oriented Programming Section 15.7.1 Virtual Destructors).
Destructors for base classes are an important exception to the rule of thumb that if a class needs a destructor, it also needs copy and assignment (§13.1.4, p. 504). A base class almost always needs a destructor, so that it can make the destructor virtual.
If a base class has an empty destructor in order to make it virtual, then the fact that the class has a destructor does not indicate that the assignment operator or copy constructor is also needed.
I don't understand the part where it mentioned "if a base class has an empty destructor, so that I can make it virtual". I thought the only way to make it virtual on a base class is to have the keyword "virtual".
Any explanation be great!
The author here considers an empty virtual destructor to be a destructor that is still user-declared (not one that is implicitly-declared as virtual because of a base class destructor) and either defined with an empty body or defaulted:
virtual ~MyClass() = default;
or
virtual ~MyClass() {}
It is impossible to force an implicitly-declared destructor to be virtual (if there isn't already a base class with a virtual destructor) and so either of these are the only possibilities to make the destructor virtual but have it behave the same way that the implicitly-defined (non-virtual) one would. That's why the author says "in order to make it virtual".
The quote is saying that having either of these in a class doesn't indicate that the class semantically needs to define its own copy assignment operator or copy constructor. The destructor still does exactly the same as if it was implicitly-defined and so the implicitly-defined copy operations are likely to still be semantically correct.
A non-empty destructor, e.g. one that performs some non-trivial work with the class (not just something like simple logging etc.) is usually an indication that the implicitly defined copy operations will not be semantically correct for the intended behavior of the class, which is why there is the mentioned rule-of-three saying that in such situations copy operations should be manually defined as well to avoid the unintended semantics.
However, in connection with move semantics there is still a problem with an empty virtual destructor. Declaring any destructor in any way will inhibit implicit declaration of the move constructor and move assignment operator.
As a consequence, if your class has e.g. a std::vector member, it will become inefficient, since it can never be moved, only copied. Usually a class with virtual destructor is used polymorphically and not copied/moved around, but that is not always the case. So it may make sense to still explicitly default all move and copy operations (in line with the rule-of-five) in such a situation:
virtual ~MyClass() = default;
MyClass(const MyClass&) = default;
MyClass(MyClass&&) = default;
MyClass& operator=(const MyClass&) = default;
MyClass& operator=(MyClass&&) = default;
Or (probably more likely), given that the class may be intended to only be used polymorphically, explicitly delete all of them to make it impossible for object slicing to occur unintentionally (see C++ core guidelines C.67):
virtual ~MyClass() = default;
MyClass(const MyClass&) = delete;
MyClass(MyClass&&) = delete;
MyClass& operator=(const MyClass&) = delete;
MyClass& operator=(MyClass&&) = delete;
First, it means that, for a base class Base, you'd likely need a virtual destructor:
class Base {
public:
virtual ~Base() {} // note the empty body
};
This is so that deleting a descendant via base pointer calls the proper ctor and frees memory correctly. Note that copy/assignment is not needed here; at this point, we don't even know if the descendant class is expected to have data members or simply fulfills an interface without any data.
This is, however, only true if you're using naked and some of the smart pointers (e.g. std::unique_ptr<>). When you use std::shared_ptr<>, your destructor is automatically saved so you don't need virtual destructors to keep a std::shared_ptr<Descendant> in a std::shared_ptr<Base> before destroying it.

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.

Can a derived class be made uncopyable by declaring copy constructor/operator private in base class?

I thought in theory the answer to this question was yes.
However, in practice, my compiler (VS2010) does not seem to complain in the following situation: I have an abstract base class providing some common interface (yet having no data members) and various sub and subsubclasses derived from it.
class Base
{
public:
Base() {}
virtual ~Base() {}
virtual void interfaceFunction1() = 0;
virtual void interfaceFunction2() = 0;
private:
Base(const Base&); // all derived classes should be uncopyable
Base& operator=(const Base&);
// no data members
};
My compiler found it unproblematic to even implement full copy constructors in sub- or subsubclasses.
How can I make sure that every class derived from Base is uncopyable?
edit: If I understand well, this is exactly what Scott Meyers explained in item 6 of Effective C++ (3rd edition, 2005) with his idea of the class Uncopyable (only extended here to a full interface class). What is the difference that makes his idea work ? (I know that he inherits privately, but this should not pose a problem)
This should prevent the compiler from generating a copy constructor for derived classes which do not declare one explicitly. However, nothing prevents a derived class from explicitly declaring a copy constructor which will do something else than call the copy constructor of Base.
There is no way to make sure derived classes are instantiable but not copyable.
Rather than declaring the copy constructor/operator as private declare them as deleted. Declaring copy constructor/operator as private is not the best solution to making the derived classes non-copyable. If you want the base class to be completely non-copyable then declare the copy constructor/operator as deleted as copy can still take place inside the member functions of Base as private members are accessible to that class's functions. You can use the C++11 feature of delete:
Base(const Base&) = delete; // copy constructor
Base& operator=(const Base&) = delete; // copy-assignment operator
But declaring copy constructor/operator as private is also right as long as you're aware that copy can still take place inside the member functions of Base.
In C++11 and later there is the option to declare a constructor deleted.
struct X {
X( const X& ) = delete;
};
Now, anything derived from X that rely on the copy-constructor will not compile.
This is most useful when you want to avoid problems because the compiler auto-generates constructors...

Why would you "default" a copy/move constructor or a destructor?

C++0x lets you specify certain functions as defaulted:
struct A {
A() = default; // default ctor
A(A const&) = default; // copy ctor
A(A&&) = default; // move ctor
A(Other); // other ctor
~A() = default; // dtor
A& operator=(A const&) = default; // copy assignment
A& operator=(A&&) = default; // move assignment
};
The implementation of these functions is the same as if the compiler generated them, something that normally happens under most circumstances when you don't declare your own.
A default ctor is not generated if you declare any ctor (any of the others above), so you might need to default it to "bring it back."
However, unless a base or data member precludes them, a class always has a copy and move ctor⁠—⁠and if they are precluded, the default implementation won't work. A class always has a dtor.
Why would you need to explicitly default a copy ctor, move ctor, or destructor? Wouldn't the implicitly generated implementations do the same thing, anyway?
You might need to do this to change their access to non-public or to control which translation unit defines them.
Non-public
Even though these functions are commonly public, you may wish them to be non-public while still desiring the default implementation:
struct A {
protected:
~A();
private:
A();
A(A const&);
A(A&&);
};
// according to N3092, §8.4.2/2, cannot be non-public and defaulted
// in the class definition
A::~A() = default;
A::A() = default;
A::A(A const&) = default;
A::A(A&&) = default;
This class can be default-constructed, copied, and moved, but only by methods and friends of A. This is useful for factories, where construction might be more tightly controlled.
A protected destructor is the second half of public-virtual/protected-nonvirtual guideline for base classes:
Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.
Definition control
Additionally, defaulted functions can be used to maintain a stable binary interface, since you have control over where the defaulted functions are defined. Defaulted doesn't imply inline, as the implicitly declared versions would be. (In the code above, the defaulted functions must either not be in a header or have the inline specifier added.)
In addition to the functional purposes, I find it useful for clarity. It makes it clear that the structure is meant to be default constructable (or whatever), and that we're using the compiler generated behavior. The more self documenting your code is, the better.