当std::is_base_of<A, B>
是模板类时,有没有办法测试A
?
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&lt; X&gt;派生自A&lt; X, char &gt;)
解决的一种方法是从(非模板)类派生A说C
,然后检查std::is_base_of<C, B<int>>
。但还有另一种方法吗?
答案 0 :(得分:16)
您可以执行以下操作:
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*>()));
但会因多次继承或来自A
的私有继承而失败。
使用Visual Studio 2017,当基类模板具有多个模板参数时,这将失败,并且无法推断Ts...
重构解决了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;
答案 1 :(得分:1)
以下解决方案适用于受保护的继承。
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&lt; int&gt;和C&lt;浮动&gt;都。 (不知道怎么做!)
答案 2 :(得分:1)
在这里参加聚会有点晚,但我想提供上述内容的一种变体
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>> );
答案 3 :(得分:0)
根据Evgeny Mamontov的回答,我认为正确的解决方案是
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));
};