2014-09-15 2 views
10

В современном 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

@Nim Как его использовать? Имеет ли он достаточные отличия от стандартной черты типа std :: is_base_of? – Orient

+0

Извините, только что понял ваш вопрос после переформатирования, проигнорируйте форсированную часть комментария. – Nim

+0

Я думаю, что-то вроде 'template constexpr std :: true_type helper (B < types... > const &); template constexpr std :: false_type helper (T &&); decltype (helper (std :: declval < D const & >())) возможно, если разрешена такая перегрузка. – Orient

ответ

10

Если вы можете предположить, что производный тип использует открытое наследование от 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

+0

Это очень похоже на стили стиля C++ 03. Есть ли какая-то конкретная причина для этого? – dyp

+0

нет, просто привычка –

+0

@dyp, как выглядят стили стиля C++ 11/14? – Niall

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 на фактическую специализацию.

Смежные вопросы