Question

The code I'm working with has its own smart pointer implementation which does simple reference counting. Yes, we shouldn't have our own implementation. Yes, we should be using one from boost or some such. Bear with me.

I found I wanted to write code like this:

...
CountedPointer<Base> base;
...
CountedPointer<Derived> derived;
...
base = derived;

However, the copy constructor for CountedPointer has a prototype like this:

CountedPointer(const CountedPointer<T> &other);

So the above code won't compile since it can't find a suitable constructor (or assignment operator - it's the same story there). I tried re-writing the copy constructor with a prototype like this:

template<U>
CountedPointer(const CountedPointer<U> &other);

However, I hit the problem that the copy constructor must access a private member of the object it's copying (i.e. the raw pointer) and if it's in a different specialisation of CountedPointer, they're not visible.

Alexandrescu avoids this problem in his library Loki by having accessor functions for the encapsulated pointer, but I'd prefer not to give direct access to the raw pointer if possible.

Is there any way I can write this to allow the derived to base copy, but not allow general access to the raw pointer?

Update: I've implemented the accepted answer below, and it works well. I spent a while figuring out why my program seg-faulted horribly when I only provided the templated version of the copy constructor, replacing the original un-templated version. Eventually, I realised that the compiler doesn't regard the templated version as being a copy constructor, and provides a default one. The default one just dumbly copies the contents without updating the counter, so I end up with dangling pointers and double frees. The same sort of thing applies to the assignment operator.

Was it helpful?

Solution

Can't you make them friends? Like:

template<typename T>
class CountedPointer {

    // ...

    template<U>
    CountedPointer(const CountedPointer<U> &other);

    template<typename U> friend class CountedPointer;
};

OTHER TIPS

"Alexandrescu avoids this problem in his library Loki by having accessor functions for the encapsulated pointer, but I'd prefer not to give direct access to the raw pointer if possible"

I think the cost of adding a raw pointer getter is going to be much less than the complexity cost of trying to get around not having raw access. There just isn't a language mechanism to convert instances between two unrelated template classes. To the compiler they are two completly different things with no relationship at run time. That's why you one template class instance can't access the others privates.

You could consider creating such a relationship with a base class for all CountedPointers. You might have to put a void* in this base class. Then you'd have to do all the checking yourself (is T derrived from U then force the cast... Can I implicitly convert a T to a U?, if so force a conversion.. etc) but that may get pretty complex. Here's a rough start for this approach:

class CountedPointerBase
{
    void* rawPtr;
};

template <class T>
class CountedPointer : public ConutedPointerBase
{
      T* myRawT = reinterpret_cast<T*>(rawPtr);

      template<class U>
      CountedPointer( CountedPointer<U> u)
      {
           // Copying a CountedPointer<T> -> CountedPointer<U>
           if (dynamic_cast<U*>(myRawT) != NULL)
           {
               // Safe to copy one rawPtr to another
           }
           // check for all other conversions
      }
}

There may be a lot of other complexities in seeing if two types are compatible. Maybe there's some Loki/Boost template slickness that can determine for two type arguments if you could cast one to another.

Anyway, as you can see, this might be a much more complex solution then just adding a getter. Being able to get the raw pointer has other benefits also. You can pass raw pointers into library functions that only accept raw pointers, and use them as temporaries, for example. It can be dangerous, I suppose, if someone on your team decides to hold onto a copy of the raw pointer as opposed to the smart ptr. That person should probably be summarily trout slapped.

You will solve the problem if you have a CountedPointer(T* other); constructor and a similar assignment operator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top