If I have a class 'Bar':
// bar.h
class Bar
{
public:
Bar() { }
};
that I forward declare to use with an std::unique_ptr in another class 'Foo':
// foo.h
#include <memory>
class Bar;
class Foo
{
public:
Foo();
private:
std::unique_ptr<Bar> bar_;
};
and whose definition I include in the implementation file of Foo:
// foo.cpp
#include "foo.h"
#include "bar.h"
Foo::Foo()
: bar_(new Bar)
{ }
I get the the compile-time error "Invalid application of 'sizeof' to an incomplete type 'Bar'".
I understand from here and here that to fix this, I can declare Foo's destructor in foo.h and move its empty definition to foo.cpp. What I don't understand is, why that fixes it. I read that std::unique_ptr sometimes requires to know the full definition of its type. It would make sense to me if I would have to include Bar from bar.h so that the unique_ptr sees its definition. But what does the destructor of Foo have to do with the visibility of Bar, and why does declaring ~Foo() in foo.h and defining it in foo.cpp silence the error?
The destructor of unique_ptr<Bar>
calls Bar::~Bar
when it delete
's the Bar
it owns. So ~unique_ptr<Bar>
needs to see Bar::~Bar
.
But template methods are only instantiated at point of use.
The unique ptr is destroyed by the Foo
in Foo::~Foo
. If ~Foo
lives where it can see the definition of ~Bar
, all is good.
If you leave it to be generated by the compiler, it 'lives' in the declaration of Foo
, where it cannot see ~Bar
.
If you forward declare it, then do a Foo::~Foo() = default
or Foo::~Foo() {}
in the .cpp
file after #include <bar.h>
, it can see ~Bar
at the point where ~std::unique_ptr<Bar>
is called`, and all is good.
This matters in practice because how Bar
is destroyed differs depending on if ~Bar
is virtual, and if Bar
has parents, and if ~Bar
is private/protected it might be illegal to call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With