This code:
/* 1 */ template <typename T> class MyClass { ... }; //template class for every T
/* 2 */ template <> class MyClass<char> { ... }; //template class full specialization for T=char
tells the C++ compiler: “The first template should be used for all instances of MyClass<T> with any T, except for the instances where T is char.”
The problem seen with this approach is that full implementations must be provided for both the template and the specialization for char (full implementation means: constructors, destructor, methods, and attribute declarations).
In practice, many situations will be encountered where only a few methods need to be specialized. The obvious solution is inheritance.
The following code snippets can be examined:
An “abstract” template class called ABase<T> is implemented:
template <typename T> class ABase
{
protected:
ABase()
{
printf("ABase<T> Constructor called\n");
}
public:
virtual ~ABase()
{
printf("ABase<T> Destructor called\n");
}
void setA(const T& aA)
{
mA = aA;
}
protected:
T mA;
};
As can be seen, this class does not provide a getA() method. Two implementations for getA() are intended: a default implementation and a specialized implementation for char. Therefore, A<T> and A<char> are implemented:
template <typename T> class A : public ABase<T>
{
public:
A() : ABase<T>()
{
printf("A<T> constructor called\n");
}
~A() override
{
printf("A<T> destructor called\n");
}
inline const T& getA() const
{
printf("A<T>::getA() called\n");
return ABase::mA;
}
};
// A specialization for T = char
template <> class A<char> : public ABase<char>
{
public:
A() : ABase<char>()
{
printf("A<char> constructor called\n");
}
~A() override
{
printf("A<char> destructor called\n");
}
inline const char& getA() const
{
printf("A<char>::getA() called\n");
return ABase::mA;
}
};
In this somewhat unhelpful example, it can be seen that two different implementations will be called when instances of A<T> are created:
objInt.getA(); will call the A<T>::getA() implementation, andobjChar.getA(); will call the A<char>::getA() specialization.
A<int> objInt;
A<char> objChar;
This is seen as “static polymorphism” or something like “compile-time polymorphism.”
The idea of using inheritance to provide several different methods can be useful for certain scenarios, such as:
template <typename T> class StringBase { ... };
and
template <> class String<char> : public StringBase<char> { ... }; //providing a method const char* c_str() const;
template <> class String<wchar_t> : public StringBase<wchar_t> { ... }; //providing something like const wchar_t* w_str() const;
Continuing with this example, the most fascinating thing about this is when inheritance from A is used:
template <typename T>
class B : public A<T>
{
...
};
When an instance of B<T> is created, for example, B<int> bInt;, bInt.getA(); will call the A<T>::getA(); method. However, if an instance of B<char> is created, for example, B<char> bChar;, bChar.getA(); will call the A<char>::getA(); specialization.
Quite powerful!
Thank you for a great post.
I quite like the helpful information you supply in your posts.
inline const char& GetA() const
{
printf(“A::GetA() called\n”);
return ABase::mA;
}
How do you place that in a source file? I’ve search alot and can’t figure out how. I reckon now that it is specialize, it is now possible to place it in a soruce file without shinanigans.
Could you please elaborate on your question? I do not get it (sorry my non-native English).