Чтобы проверить, является ли какая-либо специализация класса шаблона базовым классом определенного класса

В современном STL есть std::is_base_of. Это позволяет нам определить, является ли второй параметр производным от первого параметра или если они являются одними и теми же классами, либо в противном случае, чтобы определить, нет ли такого отношения между ними.

Можно ли определить, является ли один класс производным от какого-либо конкретного класса шаблона, без различия конкретных конкретных параметров, связанных с его специализацией?

Скажем, мы имеем:

template< typename ...types >
struct B {};

и

template< typename ...types >
struct D : B< types... > {};

Можно ли определить черту типа:

template< typename T > is_derived_from_B;

Так, что он получен из std::true_type, когда T - любая специализация D и получена из std::false_type, если T не получена из какой-либо специализации B?

Ответ 1

Если вы можете предположить, что производный тип использует общедоступное наследование от B<Args...> (и, следовательно, возможно повышение уровня), вы можете использовать следующий SFINAE:

namespace detail
{
    template <typename Derived>
    struct is_derived_from_B
    {
        using U = typename std::remove_cv<
                                  typename std::remove_reference<Derived>::type
                                >::type;

        template <typename... Args>
        static auto test(B<Args...>*)
            -> typename std::integral_constant<bool
                                           , !std::is_same<U, B<Args...>>::value>;

        static std::false_type test(void*);

        using type = decltype(test(std::declval<U*>()));
    };
}

template <typename Derived>
using is_derived_from_B = typename detail::is_derived_from_B<Derived>::type;

Тесты:

static_assert(is_derived_from_B<const D<int, char, float>>::value, "!");
static_assert(!is_derived_from_B<int>::value, "!");
static_assert(!is_derived_from_B<B<int,int>>::value, "!");
static_assert(!is_derived_from_B<std::vector<int>>::value, "!");

DEMO 1

Он может быть обобщен для принятия любого шаблона базового класса:

namespace detail
{
    template <template <typename...> class Base, typename Derived>
    struct is_derived_from_template
    {
        using U = typename std::remove_cv<
                                  typename std::remove_reference<Derived>::type
                                >::type;

        template <typename... Args>
        static auto test(Base<Args...>*)
            -> typename std::integral_constant<bool
                                          , !std::is_same<U, Base<Args...>>::value>;

        static std::false_type test(void*);

        using type = decltype(test(std::declval<U*>()));
    };
}

template <template <typename...> class Base, typename Derived>
using is_derived_from_template
                = typename detail::is_derived_from_template<Base, Derived>::type;

Тесты:

static_assert(is_derived_from_template<B, const D<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, int>::value, "!");
static_assert(!is_derived_from_template<B, B<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, std::vector<int>>::value, "!");

DEMO 2

Ответ 2

Я хочу предложить другое решение, которое также будет работать в случае частного наследования. Недостатки в том, что для этого требуется изменить шаблон базового класса, и он зависит от базового класса.

Предполагая, что ваш базовый класс template< typename... Args > class Base, вам нужно добавить к нему функцию друга:

template< /*whatever*/ > class Base {
  //...

  template< typename T >
  friend std::enable_if_t<
    std::is_base_of<Base, T>::value
  > is_derived_from_Base_impl(T const&); //unevaluated-only
};

Затем вы можете написать свой признак:

template< typename T, typename Enable=void >
struct is_derived_from_Base : std::false_type { };

template< typename T >
struct is_derived_from_Base<T,
  decltype(is_derived_from_Base_impl(std::declval<T const&>()))
> : std::true_type { };

Эта черта не будет работать в Visual Studio 2015 до обновления 1, вам нужно написать что-то вроде:

namespace is_derived_from_Base_adl_barrier {
  struct no{}; //anything but void
  no is_derived_from_Base_impl(...);
  template< typename T >
  struct is_derived_from_Base : std::is_void<decltype(
    is_derived_from_Base_impl(std::declval<T const&>());
  )> { };
}
using is_derived_from_Base_adl_barrier::is_derived_from_Base;

Вещь работает, потому что поиск по имени, зависящему от имени аргумента, найдет функцию друга, несмотря на частное наследование, а функция друга (или функции, если обнаружено несколько) будет проверять is_base_of на фактическую специализацию.