Старый добрый трюк, основанный на разрешении перегрузки, должен работать и здесь:
template<template<typename...> class TT, class T>
struct is_specialization_base_of {
template<typename... Args>
static constexpr std::true_type check(TT<Args...> const&);
static constexpr std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<T>()))::value;
};
Первым параметром шаблона является имя шаблона, которое может принимать любое количество аргументов типа. Вам нужен один, но зачем себя ограничивать?
Затем мы определяем две перегрузки, одна - это шаблон, который принимает любую гипотетическую специализацию TT
, а вторая - запасной вариант, который является переменной-аргументной функцией в стиле C. Это весь механизм прямо там. Если мы вызываем check
с любым классом, который публично наследуется от гипотетической специализации TT
, первая перегрузка выбирается из-за того, как работает разрешение перегрузки. В противном случае выбран запасной вариант.
Значение нашего признака определяется типом, созданным в неоцененном контексте decltype
, там выполняется разрешение перегрузки, но поскольку это неоцененный контекст, ничего не нужно определять, только объявлять. decltype
, следовательно, возвращает тип результата.
Вот простой набор тестов :
struct C1 : A<int> {};
struct C2 : B<int> {};
struct C3 : A<int>, B<char> {};
static_assert(is_specialization_base_of<A, C1>::value);
static_assert(!is_specialization_base_of<B, C1>::value);
static_assert(!is_specialization_base_of<A, C2>::value);
static_assert(is_specialization_base_of<B, C2>::value);
static_assert(is_specialization_base_of<A, C3>::value);
static_assert(is_specialization_base_of<B, C3>::value);
Я оставлю вам упражнение по включению этой черты в вашу функцию шаблона. Я бы просто рекомендовал превратить эти if
в if constexpr
для некоторых добавленных вкусностей.