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.