There is std::is_base_of
in modern STL. It allow us to determine whether the second parameter is derived from first parameter or if they are the same classes both or, otherwise, to determine is there no such relation between them.
Is it possible to determine whether the one class is derived from some concrete template class without distinction of which concrete actual parameters involved to its specialization?
Say, we have;
template< typename ...types >
struct B {};
And
template< typename ...types >
struct D : B< types... > {};
Is it possible to define a type trait:
template< typename T > is_derived_from_B;
Such that it is derived from std::true_type
when T
is any specialization of D
and derived from std::false_type
if T
is not derived from any specialization of B
?
An individual class defines how a group of objects can be constructed, while a class template defines how a group of classes can be generated. Note the distinction between the terms class template and template class: Class template. is a template used to generate template classes.
Inheriting from a template classIt is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.
Definition of Class Trait (noun) A behavior, custom, or norm–either real or imagined–that define or reflect a class.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
If you can assume that a derived type uses a public inheritance from B<Args...>
(and so the upcasting is possible), then you can use the following SFINAE:
namespace detail
{
template <typename Derived>
struct is_derived_from_B
{
using U = typename std::remove_cv<
typename std::remove_reference<Derived>::type
>::type;
template <typename... Args>
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*>()));
};
}
template <typename Derived>
using is_derived_from_B = typename detail::is_derived_from_B<Derived>::type;
Tests:
static_assert(is_derived_from_B<const D<int, char, float>>::value, "!");
static_assert(!is_derived_from_B<int>::value, "!");
static_assert(!is_derived_from_B<B<int,int>>::value, "!");
static_assert(!is_derived_from_B<std::vector<int>>::value, "!");
DEMO 1
It can be generalized to accept any base class template:
namespace detail
{
template <template <typename...> class Base, typename Derived>
struct is_derived_from_template
{
using U = typename std::remove_cv<
typename std::remove_reference<Derived>::type
>::type;
template <typename... Args>
static auto test(Base<Args...>*)
-> typename std::integral_constant<bool
, !std::is_same<U, Base<Args...>>::value>;
static std::false_type test(void*);
using type = decltype(test(std::declval<U*>()));
};
}
template <template <typename...> class Base, typename Derived>
using is_derived_from_template
= typename detail::is_derived_from_template<Base, Derived>::type;
Tests:
static_assert(is_derived_from_template<B, const D<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, int>::value, "!");
static_assert(!is_derived_from_template<B, B<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, std::vector<int>>::value, "!");
DEMO 2
I want to propose another solution, that will also work in case of private inheritance. Downsides are that it requires you to modify the base class template and it is base class-specific.
Assuming your base class is template< typename... Args > class Base
, you need to add a friend function to it:
template< /*whatever*/ > class Base {
//...
template< typename T >
friend std::enable_if_t<
std::is_base_of<Base, T>::value
> is_derived_from_Base_impl(T const&); //unevaluated-only
};
Then, you can write your trait:
template< typename T, typename Enable=void >
struct is_derived_from_Base : std::false_type { };
template< typename T >
struct is_derived_from_Base<T,
decltype(is_derived_from_Base_impl(std::declval<T const&>()))
> : std::true_type { };
This trait won't work in Visual Studio 2015 prior to Update 1, you'll have to write something like:
namespace is_derived_from_Base_adl_barrier {
struct no{}; //anything but void
no is_derived_from_Base_impl(...);
template< typename T >
struct is_derived_from_Base : std::is_void<decltype(
is_derived_from_Base_impl(std::declval<T const&>());
)> { };
}
using is_derived_from_Base_adl_barrier::is_derived_from_Base;
The thing works because argument-name dependent lookup will find the friend function despite the private inheritance, and the friend function (or functions, if multiple are found) will then check is_base_of
on an actual specialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With