Must provide destructor in the PIMPL - c++

// main_pimpl_sample.cpp
#include "pimpl_sample.hpp"
using namespace std;
int main()
{
pimpl_sample p;
return 0;
}
// pimpl_sample.cpp
#include "pimpl_sample.hpp"
struct pimpl_sample::impl {
};
pimpl_sample::pimpl_sample()
: pimpl_(new impl) {
}
// pimpl_sample::~pimpl_sample()
// cause problem if missed
// {}
// pimpl_sample.hpp
#if !defined (PIMPL_SAMPLE)
#define PIMPL_SAMPLE
#include <boost/scoped_ptr.hpp>
class pimpl_sample {
struct impl;
boost::scoped_ptr<impl> pimpl_;
public:
pimpl_sample();
//~pimpl_sample(); cause problem if missed
void do_something();
};
#endif
~/Documents/C++/boost $ g++ --version
g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
~/Documents/C++/boost $ g++ -o main_pimpl_sample main_pimpl_sample.cpp pimpl_sample.cpp pimpl_sample.hpp
In file included from /usr/include/boost/smart_ptr/scoped_ptr.hpp:15:0,
from /usr/include/boost/scoped_ptr.hpp:14,
from pimpl_sample.hpp:6,
from main_pimpl_sample.cpp:2:
/usr/include/boost/checked_delete.hpp: In function ‘void boost::checked_delete(T*) [with T = pimpl_sample::impl]’:
/usr/include/boost/smart_ptr/scoped_ptr.hpp:80:9: instantiated from ‘boost::scoped_ptr<T>::~scoped_ptr() [with T = pimpl_sample::impl]’
pimpl_sample.hpp:8:20: instantiated from here
/usr/include/boost/checked_delete.hpp:32:58: error: invalid application of ‘sizeof’ to incomplete type ‘pimpl_sample::impl’
/usr/include/boost/checked_delete.hpp:32:58: error: creating array with negative size (‘-0x00000000000000001’)
The solution to above compilation error is to manually provide a destructor. The indicated reason is as follows:
you must still remember to define the destructor manually; the reason
is that at the time the compiler generates an implicit destructor, the
type impl is incomplete, so its destructor isn't called.
Question> I still have difficulties to absorb the above idea and would like to know a little detail why we have to provide a manual destructor here.
Thank you

TL;DR Declare an explicit destructor and implement it in a code module (not in the header file).
If you don't create the destructor, then the compiler creates an empty automatic destructor in every translation unit which tries to destroy an object of this class. You'd get equivalent behaviour if you defined an empty inline destructor in the class header.
This causes an error because the destructor is also responsible for calling destructors of all the class' fields, which - in order - needs the instantiation of the method template boost::scoped_ptr<impl>::~scoped_ptr();. This template, in turn, cannot be instantiated then because it tries to delete an object of type impl, which is only forward declared in that scope (and you need a full definition to know how to delete this object.
OTOH, if you declare the non-inline constructor in the header, its code is only generated in pimpl_sample.cpp, where also lies the definition of impl, so the scoped_ptr's destructor can be instantiated successfully.
Other translation units then only call pimpl_sample's destructor as an external method, so they don't need to generate it and instantiate scoped_ptr's destructor on their own.

Related

std::unique_ptr incomplete type error in virtual function template class

I got a small problem which i don't understand for now and can't find explanation for it. I read about how to use std::unique_ptr in PIMPL idiom and it works but.. not in one weird situation, which of course occured to me now.
The simplest - i will show a simplified code example which reproduce a problem (Compiling with VS2017 Community).
header.h ##
Forward declaration of Forward class, and template class TestForward which has virtual function returning unique_ptr.
class Forward;
using TestUniquePtr = std::unique_ptr<Forward>;
TestUniquePtr make_ptr();
template<int a>
class TestForward {
public:
virtual TestUniquePtr foo();
};
template<int a>
TestUniquePtr TestForward<a>::foo() {
return make_ptr();
}
forward.h
#include "header.h"
#include <iostream>
class Forward {
public:
~Forward() {
std::cout << "HAAA" << std::endl;
}
};
forward.cpp
#include "forward.h"
TestUniquePtr make_ptr() {
return TestUniquePtr{ new Forward };
}
main.cpp
File which does not compile due to 'can't delete an incomplete type'.
Notice that function foo is not even called in here.
So compiler should try to compile this function in this unit?
If this function is not virtual or TestForward is not a template - it works.
#include "header.h"
int main (int argc, char *argv[]) {
TestForward<3> a;
return 0;
}
I know how can i fix this - by defining my deleter which is not a template, and write its definition in forward.cpp but.. I think this should work, so please help me find out why template+virtual make it not working :(
There is so much going on here that all plays together into this error ...
First, consider this: the C++ standard says that if you do this:
struct Incomplete;
void foo(Incomplete* p) { delete p; }
this is legal, but if the full definition of Incomplete turns out to have a non-trivial destructor, the program has undefined behavior. I believe this solely for compatibility with early C-like C++ programs.
So, to improve the safety of programs, the default deleter of unique_ptr uses a "safe delete", i.e. one that fails to compile for incomplete types. This means that the instantiation of the unique_ptr destructor must be aware of the full definition of the pointed-to class.
In your program, any code that uses the TestUniquePtr destructor must therefore be aware of the full definition of Forward.
TestForward::foo uses the destructor. make_ptr returns an object. foo move-constructs its own return value from this object, and then destroys the source. (In the actual generated code, this is most likely optimized away by the return value optimization, but the code must still be valid without it.)
And where/why is TestForward<3>::foo used? Well, since it is virtual, it must be instantiated whereever the vtable of the class is instantiated. And since it is a template instantiation, the vtable is instantiated wherever the constructor is called (because the constructor needs to write the vtable pointer to the object). And the constructor is called in main.
If foo is not virtual, there's no need to instantiate it. And if TestForward is not a template, I guess you put foo into some separate source file instead of the header, so the error didn't manifest for main.
So how do you fix this?
In a typical Pimpl context, you fix this by tightly controlling who instantiates the destructor of the unique_ptr. You explicitly declare the destructor of the interface class and put the definition into the source file where the impl class definition is known.
If, however, you want to hand out unique_ptrs to your incomplete class as opaque handles, you need to replace the default deleter.
// header.h
class Forward;
struct ForwardDeleter {
void operator ()(Forward* ptr);
};
using TestUniquePtr = std::unique_ptr<Forward, ForwardDeleter>;
// forward.cpp
class Forward { ... };
void ForwardDeleter::operator ()(Forward* ptr) { delete ptr; }

incomplete type as member of std::map

I came about the same issue as described here
Can't allocate class with forward declared value in std::map member variable
in our codebase.
Hoever I also found other cases where our compiler (MSVC 2017) is able to compile this...
After fiddling around with the code I found that defining the con- & destructor in the cpp allows the files to compile.
In test.h:
#ifndef TEST_H
#define TEST_H
#include <map>
struct Incomplete;
class Test {
std::map<int, Incomplete> member;
public:
Test();
~Test();
int foo() { return 0; }
};
#endif
In test.cpp:
#include "test.h"
struct Incomplete {};
Test::Test() {}
Test::~Test() {}
In main.cpp:
#include "test.h"
int main()
{
Test test;
return test.foo();
}
Why does defining the con- & destructor in the cpp file allow member-std::map-variables to use incomplete types?
This is because declaring the class member does not require Incomplete to be complete, but invoking the std::map destructor does, because it necessarily needs to invoke the Incomplete destructor to destroy the map contents. (Invoking the default std::map constructor could require the type to be complete, depending on the implementation. I'm not sure if the spec puts any requirements on this. I can think of at least one implementation that would not require complete types.)
If you rely on the compiler to generate implicit ctors/dtors for you, that means that the type must be complete when the class definition is encountered, because that's when the compiler is going to implicitly generate the ctor and dtor. It's as though you wrote inline Test::Test() {} inline Test::~Test() {} immediately following the class definition. The dtor is implicitly going to destroy the map, which is going to destroy the map contents by calling ~Incomplete() on any stored values, which we can't do without a definition for Incomplete. And there, the whole thing falls apart and you get an error.
However, if you tell the compiler (by way of the Test ctor/dtor declarations) that you will be implementing them later, then it won't generate them, therefore no std::map ctor/dtor invocation gets compiled at that point.
Then you complete the Incomplete type prior to defining the ctor/dtor yourself, so the Incomplete ctor/dtor invocations can be successfully compiled. If you remove the definition of Incomplete then you will run into the same error.
Note that, as others have said, you can side-step this issue by storing pointers/references to the incomplete type in the map instead. A pointer or reference to an incomplete type is actually itself a complete type. However, this may not be desirable in all cases so I'm hesitant to push that solution without knowing more details about how the map will be used.

C++ destructor not being called, depending on the linking order

I ran across this issue in my application after checking it for memory leaks, and discovered that some of my classes are not being destroyed at all.
The code below is split into 3 files, it is supposed to implement a pattern called pimpl. The expected scenario is to have both Cimpl constructor and destructor print their messages. However, that's not what I get with g++. In my application, only constructor got called.
classes.h:
#include <memory>
class Cimpl;
class Cpimpl {
std::auto_ptr<Cimpl> impl;
public:
Cpimpl();
};
classes.cpp:
#include "classes.h"
#include <stdio.h>
class Cimpl {
public:
Cimpl() {
printf("Cimpl::Cimpl()\n");
}
~Cimpl() {
printf("Cimpl::~Cimpl()\n");
}
};
Cpimpl::Cpimpl() {
this->impl.reset(new Cimpl);
}
main.cpp:
#include "classes.h"
int main() {
Cpimpl c;
return 0;
}
Here is what I was able to discover further:
g++ -Wall -c main.cpp
g++ -Wall -c classes.cpp
g++ -Wall main.o classes.o -o app_bug
g++ -Wall classes.o main.o -o app_ok
It looks like the destructor is being called in one of two possible cases, and it depends on the linking order. With app_ok I was able to get the correct scenario, while app_bug behaved exactly like my application.
Is there any bit of wisdom I am missing in this situation?
Thanks for any suggestion in advance!
The goal of the pimpl idiom is to not have to expose a definition of the implementation class in the header file. But all the standard smart pointers require a definition of their template parameter to be visible at the point of declaration in order to work correctly.
That means this is one of the rare occasions where you actually want to use new, delete, and a bare pointer. (If I'm wrong about this and there's a standard smart pointer that can be used for pimpl, someone please let me know.)
classes.h
struct Cimpl;
struct Cpimpl
{
Cpimpl();
~Cpimpl();
// other public methods here
private:
Cimpl *ptr;
// Cpimpl must be uncopyable or else make these copy the Cimpl
Cpimpl(const Cpimpl&);
Cpimpl& operator=(const Cpimpl&);
};
classes.cpp
#include <stdio.h>
struct Cimpl
{
Cimpl()
{
puts("Cimpl::Cimpl()");
}
~Cimpl()
{
puts("Cimpl::~Cimpl()");
}
// etc
};
Cpimpl::Cpimpl() : ptr(new Cimpl) {}
Cpimpl::~Cpimpl() { delete ptr; }
// etc
The problem is that at the point of the definition of the auto_ptr<Cimpl> object, Cimpl is an incomplete type, that is, the compiler has only seen a forward declaration of Cimpl. That's okay, but since it eventually deletes the object that it holds a pointer to, you have to comply with this requirement, from [expr.delete]/5:
If the object being deleted has incomplete class type at the point of
deletion and the complete class has a non-trivial destructor or a
deallocation function, the behavior is undefined.
So this code runs into undefined behavior, and all bets are off.
The code violates the One Definition Rule. There's a definition of the class Cimpl in classes.h, and a different definition of the class Cimpl in the file classes.cpp. The result is undefined behavior. It's okay to have more than one definition of a class, but they must be the same.
Edited for clarity, original retained below.
This code has undefined behavior because in the context of main.cpp the implicit Cpimpl::~Cpimpl destructor only has a forward declaration of Cimpl, but the auto_ptr (or any other form of doing delete) needs a full definition to legally clean up the Cimpl. Given that it's undefined behavior no further explanation for your observations is needed.
Original answer:
I suspect that what's happening here is that the implicit destructor of Cpimpl is being generated in the context of classes.h and not having access to the full definition of Cimpl. Then when the auto_ptr tries to do its thing and clean up its contained pointer, it deletes an incomplete class, which is undefined behavior. Given that it's undefined we don't have to go any further to explain that it's perfectly acceptable for it to work in different ways depending on the link order.
I suspect that an explicit destructor for Cpimpl with a definition in a source file would solve your problem.
EDIT: Actually now that I look at it again, I believe your program violates the one definition rule as it stands. In main.cpp it sees an implicit destructor that doesn't know how to call a destructor of Cimpl (because it only has a forward declaration). In classes.cpp the implicit destructor does have access to Cimpl's definition and thus how to call its destructor.

auto-instantiated smart pointer

I am looking for a simple way to reduce header coupling in a C++ project, which comes mostly due to (overused) class composition which of course requires complete type. For example:
// header A
class A
{
B b; // requires header B
};
I have also considered interfaces and pimpl, but both imply some boilerplate code which I do not want to write/support manually (or is there a way to make this automatic?).
So I thought about replacing member with a pointer and a forward like class B* pB;, but this requires handling of object creation and deletion. Ok, I could use smart pointers for deletion (not auto_ptr though as it requires complete type at creation so say something like shared_ptr<class B> pB;), but how to be with the object creation now?
I could create the object in A's constructor, like pB = new B; but this is, again, manual, and what is worse, there could be several constructors... So I'm looking for a way to do this automatically, which would work as simple as changing B b; to autoobjptr<class B> pB; in A's definition without having to bother with pB instantiation.
I'm pretty sure this is not a new idea, so maybe you could give me a reference to a common solution or a discussion?
UPDATE: To clarify, I am not trying to break the dependency between A and B, but I want to avoid inclusion of B's header when one includes A's one. In practice, B is used in implementation of A, so a typical solution would be to create an interface or pimpl for A but I am looking for something easier for the moment.
UPDATE2: I suddenly realized that a lazy pointer such as proposed here would do the trick (too bad there is no standard implementation of this in say boost), when combined with virtual destructor (to permit incomplete type). I still do not get why there is no standard solution and feel like re-inventing the wheel...
UPDATE3: Suddenly, Sergey Tachenov came with a very simple solution (the accepted answer), though it took me half an hour to understand why it really works...
If you remove A() constructor or define it inline in the header file, the magic won't work anymore (compliation error). I guess that when you define an explicit non-inline constructor, the construction of members (even the implicit ones) is done inside the same compilation unit (A.cpp) where the type B is complete. On the other hand, if your A constructor is inline, creation of the members must happen inside other compilation units and won't work as B is incomplete there. Well, this is logical, but now I'm curious - is this behavior defined by the C++ standard?
UPDATE4: Hopefully, the final update. Refer to the accepted answer and the comments for a discussion on the question above.
At first I got intrigued by this question as it looked like something really tricky to do, and all the comments about templates, dependencies and includes made sense. But then I tried to actually implement this and found it surprisingly easy. So either I misunderstood the question or the question has some special property of looking much harder than it really is. Anyway, here is my code.
This is the glorified autoptr.h:
#ifndef TESTPQ_AUTOPTR_H
#define TESTPQ_AUTOPTR_H
template<class T> class AutoPtr {
private:
T *p;
public:
AutoPtr() {p = new T();}
~AutoPtr() {delete p;}
T *operator->() {return p;}
};
#endif // TESTPQ_AUTOPTR_H
Looks really simple and I wondered if it actually works, so I made a test case for it. Here is my b.h:
#ifndef TESTPQ_B_H
#define TESTPQ_B_H
class B {
public:
B();
~B();
void doSomething();
};
#endif // TESTPQ_B_H
And b.cpp:
#include <stdio.h>
#include "b.h"
B::B()
{
printf("B::B()\n");
}
B::~B()
{
printf("B::~B()\n");
}
void B::doSomething()
{
printf("B does something!\n");
}
Now for the A class that actually uses this. Here's a.h:
#ifndef TESTPQ_A_H
#define TESTPQ_A_H
#include "autoptr.h"
class B;
class A {
private:
AutoPtr<B> b;
public:
A();
~A();
void doB();
};
#endif // TESTPQ_A_H
And a.cpp:
#include <stdio.h>
#include "a.h"
#include "b.h"
A::A()
{
printf("A::A()\n");
}
A::~A()
{
printf("A::~A()\n");
}
void A::doB()
{
b->doSomething();
}
Ok, and finally the main.cpp that uses A, but doesn't include "b.h":
#include "a.h"
int main()
{
A a;
a.doB();
}
Now it actually compiles with no single error nor warning and works:
d:\alqualos\pr\testpq>g++ -c -W -Wall b.cpp
d:\alqualos\pr\testpq>g++ -c -W -Wall a.cpp
d:\alqualos\pr\testpq>g++ -c -W -Wall main.cpp
d:\alqualos\pr\testpq>g++ -o a a.o b.o main.o
d:\alqualos\pr\testpq>a
B::B()
A::A()
B does something!
A::~A()
B::~B()
Does that solve your problem or am I doing something completely different?
EDIT 1: Is it standard or not?
Okay, it seems it was the right thing, but now it leads us to other interesting questions. Here is the result of our discussion in the comments below.
What happens in the example above? The a.h file doesn't need b.h file because it doesn't actually do anything with b, it just declares it, and it knows its size because the pointer in the AutoPtr class is always the same size. The only parts of autoptr.h that need the definition of B are constructor and destructor but they aren't used in a.h so a.h doesn't need to include b.h.
But why exactly a.h doesn't use B's constructor? Aren't B's fields initialized whenever we create an instance of A? If so, the compiler may try to inline this code at every instantiation of A, but then it will fail. In the example above, it looks like the B::B() call is put at the beginning of the compiled constructor A::A() in the a.cpp unit, but does the standard require it?
At first it seems that nothing stops the compiler from inlining fields initialization code whenever an instant is created, so A a; turns into this pseudocode (not real C++ of course):
A a;
a.b->B();
a.A();
Could such compilers exist according to the standard? The answer is no, they couldn't and the standard has nothing to do with it. When the compiler compiles "main.cpp" unit, it has no idea what A::A() constructor does. It could be calling some special constructor for b, so inlining the default one before it would make b initialized twice with different constructors! And the compiler has no way to check for it since the "a.cpp" unit where A::A() is defined is compiled separately.
Okay, now you may think, what if a smart compiler wants to look at B's definition and if there is no other constructor than the default one, then it would put no B::B() call in the A::A() constructor and inline it instead whenever the A::A() is called. Well, that's not going to happen either because the compiler has no way to guarantee that even if B doesn't have any other constructors right now, it won't have any in the future. Suppose we add this to b.h in the B class definition:
B(int b);
Then we put its definition in b.cpp and modify a.cpp accordingly:
A::A():
b(17) // magic number
{
printf("A::A()\n");
}
Now when we recompile a.cpp and b.cpp, it will work as expected even if we don't recompile main.cpp. That's called binary compatibility and the compiler shouldn't break that. But if it inlined the B::B() call, we end up with main.cpp that calls two B constructors. But since adding constructors and non-virtual methods should never break binary compatibility, any reasonable compiler shouldn't be allowed to do that.
The last reason for such compilers to not exist is that it doesn't actually make any sense. Even if the members initialization is inlined, it would just increase the code size and will give absolutely no performance increase since there still would be one method call for A::A() so why not to let this method do all the work in one place?
EDIT 2: Okay, what about inline and auto-generated constructors of A?
Another question that arises is what will happen if we remove A:A() from both a.h and a.cpp? Here's what happens:
d:\alqualos\pr\testpq>g++ -c -W -Wall a.cpp
d:\alqualos\pr\testpq>g++ -c -W -Wall main.cpp
In file included from a.h:4:0,
from main.cpp:1:
autoptr.h: In constructor 'AutoPtr<T>::AutoPtr() [with T = B]':
a.h:8:9: instantiated from here
autoptr.h:8:16: error: invalid use of incomplete type 'struct B'
a.h:6:7: error: forward declaration of 'struct B'
autoptr.h: In destructor 'AutoPtr<T>::~AutoPtr() [with T = B]':
a.h:8:9: instantiated from here
autoptr.h:9:17: warning: possible problem detected in invocation of delete
operator:
autoptr.h:9:17: warning: invalid use of incomplete type 'struct B'
a.h:6:7: warning: forward declaration of 'struct B'
autoptr.h:9:17: note: neither the destructor nor the class-specific operator
delete will be called, even if they are declared when the class is defined.
The only error message that is relevant is "invalid use of incomplete type 'struct B'". Basically it means that main.cpp now needs to include b.h, but why? Because the auto-generated constructor is inlined when we instantiate a in main.cpp. Okay, but does this always have to happen or does it depends on the compiler? The answer is that it can't depend on the compiler. No compiler can make an auto-generated constructor non-inline. The reason for that is that it doesn't know where to put its code. From the programmer's point of view the answer is obvious: the constructor should go in the unit where all other methods of the class are defined, but the compiler doesn't know which unit is that. And besides, class methods could be spread across several units and sometimes it even makes sense (like if a part of the class is auto-generated by some tool).
And of course, if we make A::A() explicitly inline either by using the inline keyword or by putting its definition inside the A class declaration, the same compilation error would occur, possibly a bit less cryptic.
The conclusion
It seems it's perfectly fine to employ the technique described above for auto-instantiated pointers. The only thing I'm not sure of is that AutoPtr<B> b; thing inside a.h will work with any compiler. I mean, we can use a forward-delcared class when declaring pointers and references, but is it always correct to use it as a template instantiation parameter? I think there's nothing wrong with that, but compilers may think otherwise. Googling didn't yield any useful results on it either.
I'm pretty sure this can be implemented in the same way as unique_ptr is implemented. The difference would be that the allocated_unique_ptr constructor would allocate the B object by default.
Note however that if you want automatic construction of the B object, it will be instantiated with the default constructor.
You could write an automatic pimpl_ptr<T> which would automatically new, delete, and copy the contained T.
A problem with your approach is that while you could avoid including the header file for B in this way, it doesn't really reduce dependencies.
A better way to reduce dependencies is by letting B derive from a base class declared in a separate header file, and use a pointer to that base class in A. You'd still need to manually create the correct descendant (B) in A's constructor, of course.
It is also very possible that the dependency between A and B is real and in that case you're improving nothing by artificially avoiding to include B's header file.
Well, you gave the best solution yourself, use pointers, and new them in the constructor... If there are more than one constructor, repeat that code there. You could make a base class which does that for you, but that would only mystify the implementation...
Have you thought about a template in class B? This can also solve your header cross-dependencies, but will ost likely increase your compile time... Which brings us to the reason you are trying to avoid these #includes. Have you measured compile time? Is it troubling? Is this the problem?
UPDATE: example for template way:
// A.h
template<class T>
class A
{
public:
A(): p_t( new T ) {}
virtual ~A() { delete p_t }
private:
T* p_t;
};
Again, this will most likely not increase compile time (B.h will need to be pulled in to create the template instance A<B>), it does hower allow you to remove the includes in the A header and source file.

Purpose of boost::checked_delete

I don't understand the purpose of boost::checked_delete. The documentation says:
The C++ Standard allows, in 5.3.5/5,
pointers to incomplete class types to
be deleted with a delete-expression.
When the class has a non-trivial
destructor, or a class-specific
operator delete, the behavior is
undefined. Some compilers issue a
warning when an incomplete type is
deleted, but unfortunately, not all
do, and programmers sometimes ignore
or disable warnings.
The supplied function and class
templates can be used to prevent these
problems, as they require a complete
type, and cause a compilation error
otherwise.
So the C++ standard allows you to delete incomplete types, which causes undefined behavior if the type has a non-trivial destructor. What? How can an incomplete type have any destructor at all? Isn't an incomplete type just a prototype?
The most common example of an incomplete type is one that has only been declared:
// this file does not include the definition of foo
class foo;
void bad(foo *f)
{
delete f; // undefined behavior if there exists foo::~foo
}
In reality, the definition of foo may look like this:
class foo
{
public:
~foo() { ... };
};
But if the top code has not 'seen' the class definition and just sees the class declaration, the code will compile.
Consider the following:
Foo.h:
#ifndef Foo_H
#define Foo_H
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
class Foo : private boost::noncopyable
{
public:
Foo();
~Foo();
void do_something_interesting();
private:
class Impl; // incomplete type
boost::scoped_ptr<Impl> impl;
};
#endif
Foo.cpp:
#include "Foo.h"
#include <string>
#include <iostream>
class Foo::Impl
{
public:
Impl() : name("Foo::Impl")
{}
void say_hi()
{ std::cout << name << " says hi!" << std::endl; }
std::string name;
};
Foo::Foo()
: impl(new Impl)
{}
Foo::~Foo()
{}
void Foo::do_something_interesting()
{ impl->say_hi(); }
Given this (contrived) example, you cannot inline either Foo::Foo or Foo::~Foo because the type is incomplete. By defining both in a context where the type Foo::Impl is a complete type, you can safely delete the type. boost::checked_delete does this safety check for you, and it's purely a compile-time cost. If you either inline Foo::~Foo or omit it entirely, you will get an error from boost::checked_delete wherever you try to destroy a Foo instance.
C++ allows you to use delete on variables that at the time are pointers to incomplete types.
struct S; // incomplete
int main() {
S* s = NULL;
delete s; // legal
}
The compiler doesn't know at that point what S really is. If it turns out S has a non-trivial destructor, then the compiler is not required to detect that problem.
Practically speaking, what probably happens is that when the compiler encounters the delete instruction on an incomplete type, it fills in a call to what it expects will be the type's ordinary compiler-generate default destructor. And if that's what the destructor turns out to be, then everything's fine. But if it turns out that S has a non-trivial destructor, or if it provides its own special method of deletion, then what the compiler filled in earlier will be wrong. The compiler, however, allowed to assume that it correctly compiled the delete instruction and never look back. When that assumption is wrong, you'll get undefined behavior.
The Boost function ensures that it's called only on complete types, thus avoiding the undefined behavior that may occur on incomplete types.