You have two classes Animal
and Dog
(where Dog
inherits from Animal
), and you have a situation where you are often expecting an animal but are sending an instance of a dog. In my particular case, I am often casting a strong pointer (std::shared_ptr<Dog>
) to an animal-expecting function (std::shared_ptr<Animal>
).
If we accept that we can make the function parameter a reference (std::shared_ptr<Animal>&
, avoiding arguments as to why you should not have strong pointers as reference parameters because of concerns of changing ownership with threads), I assume we would be safe memory-wise to cast a std::shared_ptr<Dog> dog
using reinterpret_cast<std::shared_ptr<Animal>&>(dog)
, right?
And if so, what could come up other than threading issues; such as that of the reference counting variety?
To be clear, the intent is to have a solution that would be used in many instances, where casting once isn't really a viable solution. It's more the issue that there are many objects that would have to be cast. Also, ignoring that std::unique_ptr
may or may not be a better solution.
To add a final requirement - using plain pointers would not allow me to change the original std::shared_ptr
in the case of a generalized serializer class function that is virtual and thus cannot be made templated.
I am often casting a strong pointer (
std::shared_ptr<Dog>
) to an animal-expecting function (std::shared_ptr<Animal>
).
There is no need to cast (i.e. explicitly convert). Shared pointer to derived type is implicitly convertible (1) to a shared pointer to base class.
(a) If we accept that we can make the function parameter a reference
We can only accept this assumption if a few requirements are met. Firstly, the function must not store the referenced argument beyond the scope of the function (except by copying). Secondly, the pointer passed as a reference must be a local or a temporary - or if isn't, then the function must not call any functions that could have directly or indirectly access to the referenced pointer.
Also consider that if the reference is non-const, then you cannot rely on the implicit conversion (1). Instead, you must create a separate shared pointer of correct type (you can use implicit conversion to create it and pass that. A const reference argument, or a non-reference argument do not have that problem.
(b) I assume we would be safe memory-wise to cast a std::shared_ptr dog using
reinterpret_cast<std::shared_ptr<Animal>&>(dog)
, right?
Standard-wise, your suggested cast is not safe - it has undefined behaviour. I don't see how assumption (b) follows from the assumption (a).
If it's fine to use reference to shared_ptr as an argument in your case, but you cannot avoid increment/decrement of the refcounter due to conversion (derived to base, non-const to const), I recommend using a bare pointer for the argument instead.