1
struct A1
{
    ~A1() {} // A1 is not trivially destructible
};

struct A2
{
    ~A2() = default; // A2 is trivially destructible
};

A2 is better than A1, because A2 is trivially destructible while A1 isn't.

I think maybe we can safely say:

1. The user-defined empty destructor should never be used.

2. Any user-defined empty destructor should be replaced with the defaulted one.

Am I right?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 1
    Related: https://stackoverflow.com/questions/28909598/eliminating-instantiation-of-useless-destructor-calls – πάντα ῥεῖ Apr 18 '21 at 15:56
  • 1
    I'd argue with 2 that "_Any user-defined empty destructor should be **removed**_". Unless you need to keep it as defaulted for self-documentation purpose. – Yksisarvinen Apr 18 '21 at 15:58
  • I would agree, see: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cdtor-destructors – Roxxorfreak Apr 18 '21 at 16:02
  • 2
    @Yksisarvinen The core guidelines say: "If the default destructor is sufficient, use it." So I would say: yes, replace. – Roxxorfreak Apr 18 '21 at 16:03
  • 2
    t@Roxxorfreak It seems that "default" here means "implicit". The first sentence of your linked rule says: *“Does this class need a destructor?” is a surprisingly insightful design question. For most classes the answer is “no” either because the class holds no resources or because destruction is handled by the rule of zero*. And the rule of zero says: [If you can avoid defining default operations, do](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c20-if-you-can-avoid-defining-default-operations-do) – Yksisarvinen Apr 18 '21 at 16:09
  • You can find a good explanation here already: https://stackoverflow.com/questions/13576055/how-is-default-different-from-for-default-constructor-and-destructor – Dirk Hönisch Apr 18 '21 at 16:46
  • For me, the only use case I've come across for an empty destructor is `virtual ~Base() {}` which could be `virtual ~Base() = default;`. But sometimes you need to put in `Base::~Base() = default;` in the implementing **Base.cpp** file so there's a place for the compiler to generate the vtable. – Eljay Apr 18 '21 at 17:24
  • https://www.fluentcpp.com/2017/09/22/make-pimpl-using-unique_ptr/ for a good reason – Matt Eding Apr 18 '21 at 18:45

2 Answers2

2

You can see a use case for such an empty destructor right there in your example. You can use it to force a trivial type to become non-trivial. That’s an outstandingly unlikely use case. But I wouldn’t be confident at all to call it never useful.

You’re not far off the mark, though. In a decade of programming C++ every day I’ve seen a lot of empty destructors, but not a single one that couldn’t be replaced with =default or omitted entirely.

Still, I’d be wary of a mechanical replacement. So:

  1. The user-defined empty destructor should almost never be used.

  2. Almost every user-defined empty destructor should either be omitted entirely (preferred option) or replaced with the defaulted one.

besc
  • 2,507
  • 13
  • 10
2

In the header file:

struct some_secret_type;
struct some_public_type {
  some_public_type();
  ~some_public_type();
private:
  std::unique_ptr<some_secret_type> pImpl;
};

Then, in the cpp file:

#include <some_secret_type.h>

some_public_type::~some_public_type() = default;
some_public_type::~some_public_type() {};

Here I have explicitly declared a destructor that ended up being either empty or defaulted.

The effects of =default; and {} here are identical in the cpp file.

In the header file, having either a {} or =default would require everyone including it know what some_secret_type looks like.

In a more general case, {} and =default in the header can change if a type is trivially destroyed. In the cpp/header split, they do not.

The advantage of {} in the header is that you prevent a type from being trivial (suppose later you know you are going to make it non-trivial, and you don't want other behavior changes to happen when you do it).

In the cpp file case, {} saves a few characters.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524