Thanks. I wasn't aware that auto_ptr<> required a complete definition.
I thought it was usable with incomplete types. If Herb Sutter used it
the wrong way, then I guess I'm in good company
There was something called the "grin pointer" which I found on the
internet about three years ago. I believe that the author of this code
(Alan Griffiths, IIRC) has since deprecated it in favor of the Boost
family of smart pointer types (Alan, please correct me if you are
reading this, or provide an update on the URL ... unfortunately, I've
forgotten where it was located). But it still comes in handy when you
don't want/need the Boost things. We used it pretty much exclusively
for auto-deletion with pImpl pointers, and I believe that was exactly
what it was meant to do. <g>
Essentially, you delegate destruction (e.g. call of delete on the
plain pointer) to a static member function whose address is stored in
a member function pointer. As long as the complete type is known by
the time the smart pointer is constructed, it works beautifully. That
means that you can still put just a forward-declaration of T in the
header file, but you must include T's header in the .cpp file which
includes the definition of the class where is defined which has a
GrinPtr<T> as a member.
Here's a very stripped-down version, typed from memory, so caveats are
in place:
typedef void (*DeleteType)();
template<typename T>
class GrinPtr {
private:
static void DeleteFunc(T* ptr) { delete ptr; }
T* m_ptr;
DeleteType m_DeleteFunc;
// disallow default ctor, copy ctor
// and op= by not implementing the following:
GrinPtr();
GrinPtr(GrinPtr const &);
GrinPtr& operator=(GrinPtr const &);
public:
GrinPtr(T* plain)
: m_DeleteFunc(&DeleteFunc)
, m_ptr(plain) { assert(sizeof(T)); }
~GrinPtr() { m_DeleteFunc(m_ptr); }
T* operator->() { return m_ptr; }
T const * operator->() const { return m_ptr; }
T& operator*() { return *m_ptr; }
T const & operator*() const { return *m_ptr; }
};
The assert(sizeof(T)) statement in the body of the constructor will
generate an error if T is still an incomplete type when the GrinPtr<T>
is constructed.
Of course, you can also extend it to accept a constructor argument of
type bool indicating whether or not the pointer is an array pointer
(default value of false comes in handy), or to disallow null pointers.
We even implemented a shared smart pointer, using this as our starting
point, for use in STL containers.
But you really should look at the Boost smart pointers and only resort
to rolling your own as a last resort!