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>> );
my_is_base_of<A, B<int>, char>
be a reasonable usage for you? – Quiet