19

I am Pimpling off the class STFT. Compiles just fine with this in the header:

class STFT; // pimpl off to prevent point name clash

class Whatever
{
private:
    STFT* stft;

and this in the implementation:

#include "STFT.h"
Whatever::Whatever() : stft(new STFT()) {
// blah blah
}

Whatever::~Whatever() {
    delete stft; // pure evil
}

However, switching to std::unique_ptr<STFT> stft; over the raw pointer in the header, and removing the destructor, I get

error: invalid application of 'sizeof' to an incomplete type 'STFT' static_assert(sizeof(_Tp) > 0, "default_delete can not delete incomplete type");

But if I simply supply an empty destructor Whatever::~Whatever(){}, then it compiles fine. This has me completely stumped. Please fill me in on what this meaningless destructor is doing for me.

Shafik Yaghmour
  • 143,425
  • 33
  • 399
  • 682
learnvst
  • 13,927
  • 13
  • 65
  • 108

2 Answers2

25

If we go to the cppreference document for std::unique_ptr:

std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the Pimpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr. (Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete).

We can see in the below code:

#include <memory>

class STFT; // pimpl off to prevent point name clash

class Whatever
{
    public:
     ~Whatever() ;
    private:
      std::unique_ptr<STFT> stft;
} ;

//class STFT{};

Whatever::~Whatever() {}

int main(){}

The requirements are not fulfilled when the defintion of STFT is commented before the destructor of Whatever is defined since this requires the destructor for stft which in turn requires STFT to be complete.

So it seems likely that in your implementation file STFT is complete when Whatever::~Whatever() is defined but otherwise the defaulted one is created without the STFT being complete.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
Shafik Yaghmour
  • 143,425
  • 33
  • 399
  • 682
11

My usual idiom for providing such destructors is

Whatever::~Whatever() = default;

The important thing is that it needs to be somewhere where the pointed-to type(s) are complete.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
  • 4
    Using that syntax gives same error. I need to explicitly declare an empty dtor. – learnvst Dec 03 '15 at 18:14
  • 4
    That depends **where** you put it. If you put it in the header, where `STFT` is incomplete, then it's as bad as letting the compiler generate it where it's needed. It needs to be in the implementation file, after `#include `, where all the pointed-to types are complete. If it didn't work, I wouldn't be using it myself. – Toby Speight Dec 03 '15 at 18:24
  • Ahhhh. Ok. That works – learnvst Dec 03 '15 at 18:26
  • 1
    Is there any difference whatsoever compared to `Whatever::~Whatever() {}`? That one is 7 characters shorter :-P – Nikos C. Aug 26 '17 at 10:18
  • 2
    Technically, there is a different between an *explicitly-defaulted* destructor and a *user-supplied* empty destructor, but I can't think of a practical difference right now. I find the explicit `default` faster to read, and damn the expense! – Toby Speight Aug 28 '17 at 18:16