2014-09-17 4 views
3

Вопрос, возникший в связи с недавно возникшим question about extended std::is_base_of type trait.Техника для различения обычных параметров шаблона и параметров шаблона шаблона с использованием разрешения SFINAE

Есть ли метод, который позволяет различать между обычным параметром шаблона и шаблоном параметрами шаблона в современной C++ или его расширения (скажем, -std=gnu++1zлязга ++/г ++)?

namespace details 
{ 

template< /* ??? */ base > 
struct is_derived_from; 

template< typaneme base > 
struct is_derived_from<base> 
{ 
    static std::true_type test(base *); 
    static std::false_type test(void *); 
}; 

template< template< typename ...formal > base > 
struct is_derived_from< /* ??? */ > 
{ 
    template< typename ...actual > // actual parameters must be here! 
    static std::true_type test(base<actual...> *); 
    static std::false_type test(void *); 
}; 

} // namespace details 

template< typename derived, /* ??? */ base > 
using is_derived_from = decltype(details::is_derived_from< /* ? base<?> */ >::test(std::declval< typename std::remove_cv<derived>::type * >())); 

В положительном случае это позволяет нам сделать некоторые из полезных признаков типа гораздо более мощным (например, STL-х std::is_base_of).

Я думаю, что для этого требуется функция языка как «обобщенные типы имен», не так ли?

+0

Вы не можете перегружать шаблоны классов. Я думаю, вы можете использовать перегрузку шаблонов 'constexpr'. –

+0

@ T.C. Существуют ли фундаментальные различия между ними, которые мы можем использовать? – Orient

+0

@ T.C. Вам не нужно 'constexpr', достаточно сделать перегрузочное разрешение внутри' decltype' без оценки. – Potatoswatter

ответ

3

Для шаблонов классов может быть только один набор параметров шаблона, но вы можете использовать перегрузку constexpr шаблонов функций вместо того, чтобы отправлять соответствующий шаблон класса. Возьмите is_derived_from черты в linked question, с дополнительным параметром SFINAE, так что вы не получите жесткую ошибку, когда B является недостижимым или неоднозначным основанием:

#include <type_traits> 
namespace detail 
{ 
    template <template <class...> class B, typename Derived> 
    struct is_derived_from 
    { 
     using U = typename std::remove_cv<Derived>::type; 

     template <typename... Args, 
        typename = std::enable_if_t< 
          std::is_convertible<U*, Base<Args...>*>::value>> 
     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*>())); 
    }; 

    using std::is_base_of; // may want to use is_convertible instead to match 
          // the semantics of is_derived_from 
} 

template <template <class...> class B, typename Derived> 
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; } 

template <class B, typename Derived> 
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; } 

struct B {}; 
struct D : B {}; 

template<class ...> 
struct B2 {}; 
struct D2 : B2<int, double> { }; 

int main() { 
    static_assert(my_is_base_of<B2, D2>(), "Oops"); 
    static_assert(my_is_base_of<B, D>(), "Oops"); 
    static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops"); 
    static_assert(!my_is_base_of<B, D2>(), "Oops"); 
} 

Demo.

+0

'struct BB: B < struct X >, B < struct Y > {};' не вызывает жесткой ошибки ** clang ++ **. – Orient

+0

@Orient Это интересно - но не совсем то, что я имел в виду, когда говорил о двусмысленных основаниях. –

+0

Возможно, это так из-за [новой модели памяти] (http://talesofcpp.fusionfenix.com/post-18/episode-ten-when-size-does-matter). – Orient

1

Вы спросили:

Есть ли метод, который позволяет нам отличие между обычным параметром шаблона и шаблона параметров шаблона в современном C++ или его расширений (скажем, -std=gnu++1z лязг ++/г ++)?

Кажется мне, как вам нужно что-то вроде:

template <typename T> 
struct is_template_template : public std::false_type 
{ 
}; 

template <typename T1, template <typename T> class T2> 
struct is_template_template<T2<T1>> : std::true_type 
{ 
}; 

Пример программы

#include <iostream> 

template <typename T> 
struct is_template_template : public std::false_type 
{ 
}; 

template <typename T1, template <typename T> class T2> 
struct is_template_template<T2<T1>> : std::true_type 
{ 
}; 

template <typename T> struct A {}; 
struct B {}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << is_template_template<A<int>>::value << std::endl; 
    std::cout << is_template_template<B>::value << std::endl; 
    return 0; 
} 

Выходные:

 
true 
false 
+0

Вы неправильно поняли мои вопросы, но я виновен в этой ситуации (из-за моего плохого знания английского языка). – Orient

+0

Просьба привести пример использования вашей конструкции в реальной жизни. Типовой признак должен иметь возможность идентифицировать некоторое значащее нетривиальное свойство параметра (ов) шаблона. – Orient

+0

Фактический параметр не может быть «шаблоном шаблона». Мы не можем предоставить 'is_template_template < A > :: value', то есть не можем проверить, является ли идентификатор именем класса шаблона. – Orient

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