Template specialization priorities when using pointers or references
-
10-10-2019 - |
Question
I have a Serializer class like this:
class Serializer
{
public:
// Func 1 (default)
template <class T>
void Serialize(T* pValue)
{
SerializeInternal(reinterpret_cast<char*>(pValue), sizeof(*pValue));
}
// Func 2 (specialization)
template <>
void Serialize<Serializable>(Serializable* pSerializable)
{
pSerializable->Serialize(*this);
}
protected:
// Implemented by input and output serializers
virtual void SerializeInternal(char* pData, size_t size) = 0;
};
Now my problem is when I have classes that inherit the Serializable interface they will always be handled by Func 1, even though I want them to be handled by Func 2 (pointers or references doesn't matter they both behave equally). It seems like C++ doesn't recognize that the Serializable interface is inherited unless you clearly specify that:
SerializableClass sc; // Inherits Serializable
InputSerializer s; // Inherits Serializer
s.Serialize(&sc); // Func 1 is called >:(
s.Serialize<Serializable>(&sc); // Func 2 is called
Now as soon as I forget to add <Serializable>
somewhere the program of course bugs out, which is pretty annoying.
Is there any way around this?
Solution
It seems like C++ doesn't recognize that the Serializable interface is inherited unless you clearly specify that
This is true. If you have some class
class SerializableClass : public Serializable
only SerializableClass
, not Serializable
, is considered when deducing the T
parameter.
If what you need is create two functions, one taking any pointer, the other taking a pointer to anything derived from Serializable, you can create two overloads and use SFINAE to select the narrower one when possible.
template <class T>
typename boost::enable_if_c<!boost::is_base_of<Serializable, T>::value, void>::type foo(T*) { ... }
template <class T>
typename boost::enable_if<boost::is_base_of<Serializable, T>, void>::type foo(T*) { ... }
If you don't want to use boost, you can implement required functionality akin to this.
OTHER TIPS
Use an overload instead of a template specialization!
I found a link explaining how boost::is_base_of
works: How does `is_base_of` work?
Apparently they use some pretty fancy template-fu magic to get it to work. I can "easily" write a similar function myself.
When you're not clever enough to solve it yourself, look at the pros ;)