c++ std::is_base_of用于模板类

pkmbmrz7  于 2023-05-20  发布在  其他
关注(0)|答案(4)|浏览(183)

A是一个模板类时,有没有一种方法可以测试std::is_base_of<A, B>

template <typename X, typename Y> class A {};

template <typename X> class B : public A<X, char> {};

我想静态地测试一些东西,比如,std::is_base_of<A, B<int>>,意思是,B是从A的任何特殊化中派生出来的。(为了使其更一般化,假设我们不知道B专门化A的方式,即B派生自A<X,char>)
一种解决方法是从一个(非模板)类(比如C)派生A,然后检查std::is_base_of<C, B<int>>。但还有别的办法吗?

k5ifujac

k5ifujac1#

您可以执行以下操作:

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
但会因A继承或私有继承而失败。
在Visual Studio 2017中,当基类模板具有多个模板参数并且无法推断Ts...时,这将失败
Demo
VS Bug Report
重构解决了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

dzhpxtsq

dzhpxtsq2#

有点晚了党在这里,但我想给予一个变种以上

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;

这使用了建议的is_detected机制,我认为这使得测试的意图更清晰一些。然而,我现在可以同时获得基类示例化的类型,我发现这很有用。这样我就能写作了

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>> );
7vhp5slm

7vhp5slm3#

以下解决方案适用于受保护的继承。

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;
};

令人惊讶的是,在VS2017上,它可以从同一个模板进行多重继承,比如C< int >和Cboth< float >。)
检查Link to test code

w6lpcovy

w6lpcovy4#

根据叶夫根尼·马蒙托夫的回答,我认为正确的解决方案是

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));
};

相关问题