Специализация шаблона не учитывает иерархию наследования. Например, если я специализирую шаблон для Base
и создаю его с помощью Derived
, специализация не будет выбрана (см. Код (1) ниже).
Это может быть серьезным препятствием, поскольку это иногда приводит к нарушению принципа замещения Лискова. Например, при работе над этим вопросом я заметил, что не мог использовать алгоритмы Boost.Range с std::sub_match
, пока я мог с std::pair
. Поскольку sub_match
наследуется публично из pair
, здравый смысл будет определять, что я могу заменить sub_match
всюду, а pair
, но это не удается из-за классов признаков с использованием специализированной специализации.
Мы можем преодолеть эту проблему, используя специализированную специализацию по шаблону вместе с enable_if
и is_base_of
(см. код (2)). Должен ли я всегда одобрять это решение по полной специализации, особенно при написании кода библиотеки? Есть ли недостатки в этом подходе, которые я наблюдал? Это практика, которую вы часто используете или видели часто?
Примеры кодов
(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"
}