c++shared-ptrsmart-pointers

How to cast my custom shared pointer in C++


I am creating a custom shared pointer in C++ to learn how these things work.

My implementation is not general purpose, I only want it to be used with a class Foo or an any of its subclasses.

This is what my classes look like:

template <typename T, typename = std::enable_if_t<std::is_same_v<T, Foo> || std::is_base_of_v<Foo, T>>>
class ReferenceCounter final
{
private:
    T* m_Value;
    unsigned int m_Counter;
};


template <typename T, typename = std::enable_if_t<std::is_same_v<T, Foo> || std::is_base_of_v<Foo, T>>>
class SharedPtr final
{
private:
    ReferenceCounter<T>* m_ReferenceCounter;
};

I have also implemented all the constructors, copy constructors, assignment operators, etc. I didn't show them here because it is a lot of code and I don't think it is relevant to the question.

Now I want to add two functionalities:

How can this be implemented?


Solution

  • This is not possible with the given design as stated in the comments. The SharedPtr<Foo> would have to store a pointer to the ReferenceCounter<FooDerived> provided by MakeShared<FooDerived>, which is not possible. The reference counting mechanics should not be templated to make this work.

    class ReferenceCounter {
        ...
        unsigned m_Count;
    };
    
    template<class T, ...>
    class SharedPtr {
        ...
        T* m_Value;
        ReferenceCounter* m_Ref;
    };
    

    Now, you have to create a converting constructor and do the necessary reference adjustments.

    // Within the SharedPtr class
    
    template<class Other, /* Only if T is a base of Other */>
    SharedPtr(const SharedPtr<Other>& ptr) :
        m_Value(ptr.m_Value),
        m_Ref(ptr.m_Ref)
    {
        // Increment the reference count, remember to check if ptr holds a non-null value/ref.
        ++m_Ref.m_Count;
    }
    
    // Also remember to friend the other class instantiations of SharedPtr
    template<class Other>
    friend class SharedPtr;
    

    To cast base to derived, you will have to try something with dynamic_cast.

    template<class To, class From, /* Only if Foo is a base of To and From */>
    auto TryCast(const SharedPtr<From>& ptr)
    {
        auto* value = /* Retreive m_Value from ptr */
        auto* ref = /* Retreive m_Ref from ptr */
    
        auto* new_value = dynamic_cast<To*>(value);
        if (!new_value)
            return SharedPtr<To>(nullptr);
    
        return /* SharedPtr<To> with m_Value = new_value and m_Ref = ref */
        // Remember to adjust the refcount
    }