Template specialization does not take into account inheritance hierarchy. For example, if I specialize a template for Base
and instantiate it with Derived
, the specialization will not be chosen (see code (1) below).
This can be a major hindrance, because it sometimes lead to violation of the Liskov substitution principle. For instance, while working on this question, I noticed that I could not use Boost.Range algorithms with std::sub_match
while I could with std::pair
. Since sub_match
inherits publicly from pair
, common sense would dictate that I could substitute a sub_match
everywhere a pair
is used, but this fails due to trait classes using template specialization.
We can overcome this issue by using partial template specialization along with enable_if
and is_base_of
(see code (2)). Should I always favor this solution over full specialization, especially when writing library code? Are there any drawbacks to this approach that I have overseen? Is it a practice that you use or have seen used often?
Sample codes
(1)
#include <iostream>
struct Base {};
struct Derived : public Base {};
template < typename T >
struct Foo
{
static void f() { std::cout << "Default" << std::endl; }
};
template <>
struct Foo< Base >
{
static void f() { std::cout << "Base" << std::endl; }
};
int main()
{
Foo<Derived>::f(); // prints "Default"
}
(2)
#include <type_traits>
#include <iostream>
struct Base {};
struct Derived : public Base {};
template <typename T, typename Enable = void>
struct Foo
{
static void f() { std::cout << "Default" << std::endl; }
};
template <typename T>
struct Foo<
T, typename
std::enable_if< std::is_base_of< Base, T >::value >::type
>
{
static void f() { std::cout << "Base" << std::endl; }
};
int main()
{
Foo<Derived>::f(); // prints "Base"
}