std::is_base_of for template classes

前端 未结 4 741
后悔当初
后悔当初 2020-12-09 09:31

Is there a way to test std::is_base_of when A is a template class?

template  class A {};
         


        
相关标签:
4条回答
  • 2020-12-09 10:16

    You may do the following:

    template <template <typename...> class C, typename...Ts>
    std::true_type is_base_of_template_impl(const C<Ts...>*);
    
    template <template <typename...> class C>
    std::false_type is_base_of_template_impl(...);
    
    template <typename T, template <typename...> class C>
    using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));
    

    Live Demo

    but will fail with multiple inheritance or private inheritance from A.

    With Visual Studio 2017 this will fail when the base class template has more than one template parameter, and it is unable to deduce Ts...

    Demo

    VS Bug Report

    Refactoring solves the problem for VS.

    template < template <typename...> class base,typename derived>
    struct is_base_of_template_impl
    {
        template<typename... Ts>
        static constexpr std::true_type  test(const base<Ts...> *);
        static constexpr std::false_type test(...);
        using type = decltype(test(std::declval<derived*>()));
    };
    
    template < template <typename...> class base,typename derived>
    using is_base_of_template = typename is_base_of_template_impl<base,derived>::type;
    

    Live Demo

    0 讨论(0)
  • 2020-12-09 10:18

    The following solution works with protected inheritance.

    template <template <typename...> class BaseTemplate, typename Derived, typename TCheck = void>
    struct test_base_template;
    
    template <template <typename...> class BaseTemplate, typename Derived>
    using is_base_template_of = typename test_base_template<BaseTemplate, Derived>::is_base;
    
    //Derive - is a class. Let inherit from Derive, so it can cast to its protected parents
    template <template <typename...> class BaseTemplate, typename Derived>
    struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
    {
        template<typename...T>
        static constexpr std::true_type test(BaseTemplate<T...> *);
        static constexpr std::false_type test(...);
        using is_base = decltype(test((test_base_template *) nullptr));
    };
    
    //Derive - is not a class, so it is always false_type
    template <template <typename...> class BaseTemplate, typename Derived>
    struct test_base_template<BaseTemplate, Derived, std::enable_if_t<!std::is_class_v<Derived>>>
    {
        using is_base = std::false_type;
    };
    

    Surprisingly, on VS2017, it works with multiple inheritance from the same template, like C< int > and C< float > both. (have no idea how!)

    Check the Link to test code

    0 讨论(0)
  • Based on Evgeny Mamontov answer I believe the proper solution is

    template <template <typename...> class BaseTemplate, typename Derived>
    struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
    {
        template<typename...T>
        static constexpr std::true_type test(BaseTemplate<T...> *);
        static constexpr std::false_type test(...);
        using is_base = decltype(test((Derived *) nullptr));
    };
    
    0 讨论(0)
  • 2020-12-09 10:20

    Bit late to the party here but I wanted to give a variant of the above

    template < template <typename...> class Base,typename Derived>
    struct is_base_of_template
    {
        // A function which can only be called by something convertible to a Base<Ts...>*
        // We return a std::variant here as a way of "returning" a parameter pack
        template<typename... Ts> static constexpr std::variant<Ts...> is_callable( Base<Ts...>* );
    
        // Detector, will return type of calling is_callable, or it won't compile if that can't be done
        template <typename T> using is_callable_t = decltype( is_callable( std::declval<T*>() ) );
    
        // Is it possible to call is_callable which the Derived type
        static inline constexpr bool value = std::experimental::is_detected_v<is_callable_t,Derived>;
    
        // If it is possible to call is_callable with the Derived type what would it return, if not type is a void
        using type = std::experimental::detected_or_t<void,is_callable_t,Derived>;
    };
    
    template < template <typename...> class Base,typename Derived> 
    using is_base_of_template_t = typename is_base_of_template<Base,Derived>::type;
    
    template < template <typename...> class Base,typename Derived>
    inline constexpr bool is_base_of_template_v = is_base_of_template<Base,Derived>::value;
    

    This uses the proposed is_detected machanism which I think makes the intent of the test a bit clearer. However I can now get the type(s) with which the base class is instantiated at the same time which I find useful. So I can write

    template <typename T, typename U> struct Foo { };
    
    struct Bar : Foo<int,std::string> { };
    
    static_assert( is_base_of_template_v<Foo,Bar> );
    
    // The variant type contains the types with which the Foo base is instantiated 
    static_assert( std::is_same_v<std::variant<int,std::string>,is_base_of_template_t<Foo,Bar>> );
    
    0 讨论(0)
提交回复
热议问题