views:

161

answers:

3

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?

+6  A: 

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.

R Samuel Klatchko
How is it possible to *require* a complete type in a class template?
BlueRaja - Danny Pflughoeft
`checked_delete` attempts to call `sizeof` on the type, and apparently that causes a compiler error if the type is incomplete.
Channel72
@Channel72: You happen to know why c++ does not define this as a compiler error anyway?
Viktor Sehr
@Viktor: The basic code generated for any type with a trivial destructor and no custom deallocator when you call `delete` is the same, so you don't *need* the complete type to be known for the compiler to generate the `delete` code. Rather than disallow such reasonable situations by requiring this to be an error, they chose to leave it undefined behavior if you use the ability unwisely.
Dennis Zickefoose
+2  A: 

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.

Rob Kennedy
+1  A: 

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.

Nathan Ernst