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:
SharedPtr<Foo> f = MakeShared<FooDerived>();
SharedPtr<Foo> f = ...; SharedPtr<FooDerived> fd = TryCast<FooDerived>(f);
How can this be implemented?
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
}