[Insight-developers] Problem with SmartPointer<const Self>

Brad King brad.king@kitware.com
Fri, 19 Jan 2001 15:14:17 -0500 (EST)


Hello, all:

I've looked into implementing the support for smart pointers to const
objects, and ran into a problem with making Register/UnRegister methods
const.  The UnRegister method looks like this:

void LightObject::UnRegister()
{
  if(--m_ReferenceCount <= 0)
    {
    /**
     * If there is a delete method, invoke it.
     */
    this->InvokeEvent(Command::DeleteEvent);
    delete this;
    }
}

Although the "delete this" statement is considered const, the InvokeEvent
is not.  I traced through the execution of InvokeEvent, and there is no
way to make the whole chain const because it makes Execute calls that are
allowed to modify the object.  Therefore, we cannot make the UnRegister
method const.

There is another solution.  We can have the smart pointer explicitly cast
away const-ness when it calls the register and unregister methods.  It
would be implemented as follows:

/**
 * A utility to get a non-const version of any type.
 * This uses partial specialization (T v. const T), but Visual C++ seems
 * to handle it just fine in this case.
 */
template <typename T>
struct StripConst
{
  typedef T Result;
};

template <typename T>
struct StripConst<const T>
{
  typedef T Result;
};


/**
 * Smart pointer class definition.
 */
template <typename T>
class SmartPointer
{

//......

private:
  /* The pointer to the object referrred to by this smart pointer. */
  T* m_Pointer;

  void Register()
    { 
    if (m_Pointer)
      {
      const_cast<StripConst<T>::Result*>(m_Pointer)->Register();
      }
    }
  
  void UnRegister()
    {
    if (m_Pointer)
      {
      const_cast<StripConst<T>::Result*>(m_Pointer)->UnRegister();
      }
    }
};


There is one other const v. non-const issue, in that the following code
will produce an error:

itk::Object::ConstPointer obj = itk::Object::New();

because itk::SmartPointer<Object> cannot convert to
itk::SmartPointer<const Object> since they are totally separate
instantiations of SmartPointer.  The solution to this is to have a partial
specialization (again, supported by Visual C++) of SmartPointer for const
objects which would have an extra constructor that can convert from a
non-const version.  Then, all problems would be solved by an
implementation like this:

/**
 * Smart pointer for non-const objects.
 */
template <typename T>
class SmartPointer
{
public:
  //....
  SmartPointer(const SmartPointer<T>& r) // copy constructor...
  //....
private:
  T* m_Pointer;
      
  void Register()
    {  
    if (m_Pointer)
      {
      // T is non-const, don't need to cast.
      m_Pointer->Register();
      }
    }

  void UnRegister()
    {
    if (m_Pointer)
      {
      // T is non-const, don't need to cast.
      m_Pointer->UnRegister();
      }
    }
};

template <typename T>
class SmartPointer<const T>
{
public:
  //....
  SmartPointer(const SmartPointer<const T>& r) // copy constructor...

  /**
   * Construct from SmartPointer to non-const T.
   */
  SmartPointer(const SmartPointer<T>& r):
    m_Pointer(r.GetPointer())
    {
    this->Register();
    }

  //....
private:
  const T* m_Pointer;
      
  void Register()
    {  
    if (m_Pointer)
      {
      // T is non-const, but m_Pointer is "const T", so cast away const.
      const_cast<T*>(m_Pointer)->Register();
      }
    }

  void UnRegister()
    {
    if (m_Pointer)
      {
      // T is non-const, but m_Pointer is "const T", so cast away const.
      const_cast<T*>(m_Pointer)->UnRegister();
      }
    }
};

Note that the StripConst is no longer needed because the T in the
"const T" version of the class doesn't have a const qualifier.

I recommend this version as the new implementation for smart pointers to
solve all const-related problems encountered so far.

Thoughts?
-Brad