How to declare the virtual destructor without breaking move and copy constructors
Asked Answered
I

1

5

When adding a user defined default virtual destructor to a class like this..

class Foo
{
public:
    Foo();
    virtual ~Foo() = default;
};

.. It has the side effects of preventing auto generation of move constructors. Also auto generation of copy constructors is deprecated. A recommended way is to user define all constructors like this..

class Foo
{
public:
  Foo();
  virtual ~Foo() = default;
  Foo(const Foo& /* other */) = default;
  Foo&operator=(const Foo& /* other */) = default;
  Foo(Foo&& /* other */) = default;
  Foo&operator=(Foo&& /* other */) = default;
};

However, this is super verbose and unreadable. Are there any other solutions to this?

Isidor answered 1/2, 2016 at 10:0 Comment(4)
If you have a virtual dtor, you probably want to delete all copy and move ctors anyway.Wreckage
And potentially have a clone() method instead.Haematoxylin
I would argue that you want those last four lines in all classes anyway (possibly with = delete rather than = default) - just to be clear about what the class is providing. (Arguably if you delete the "copy" functions, you don't need to mention the "move" functions.)Ruffi
@MartinBonner I think all the last five last lines should be avoided if at all possible. By raii design that is almost alway possible. Duplicating five lines of boilerplate all over the place do not add to clarity. But if you add one, then add the rest!Isidor
I
5

First I would consider whether Foo really needs a virtual destructor. Maybe you can solve your problem in a type safe manner using a simple template, saving you from messing with pointers and casting and so on.

If you decide on making Foo virtual, then I would recommend this abstraction.

class VirtualDestructor
{
protected:
  VirtualDestructor() = default;
  virtual ~VirtualDestructor() = default;
  VirtualDestructor(const VirtualDestructor & /* other */) = default;
  VirtualDestructor &operator=(const VirtualDestructor & /* other */) = default;
  VirtualDestructor(VirtualDestructor && /* other */) = default;
  VirtualDestructor &operator=(VirtualDestructor && /* other */) = default;
};

Put this in a library in an appropriate namespace. Then you can keep Foo and all other virtual classes clean.

class Foo : VirtualDestructor
{
public:
    Foo();
};

The same technique can also be used when deleting for example copy constructors.

Edit: Compiler output and diff with original code

Isidor answered 1/2, 2016 at 10:0 Comment(3)
Can you elaborate on the difference, if any, in the behavior of Foo when having these = default definitions directly in it, and its behavior when it inherits them from VirtualDestructor?Flavius
@Flavius Updated answer with compiler output of the two versions.Isidor
virtual destructor does not suppress operator= so I would suggest removing those from your class (your choices impose a condition on derived classes, they might have wanted to use copy-and-swap for example)Hamid

© 2022 - 2024 — McMap. All rights reserved.