Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::is_base_of for template classes

Is there a way to test std::is_base_of<A, B> when A is a template class?

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

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

I want to statically test something like, std::is_base_of<A, B<int>> meaning, B is derived from any specialization of A. (To make it more general, let's say we don't know the way B specializes A, i.e. B<X> derives from A<X, char>)

One way to solve would be to derived A from a (non-template) class say C, and then check std::is_base_of<C, B<int>>. But is there another way to do this?

like image 586
Saswat Padhi Avatar asked Jan 08 '16 08:01

Saswat Padhi


People also ask

What is the syntax for class template?

What is the syntax of class template? Explanation: Syntax involves template keyword followed by list of parameters in angular brackets and then class declaration. As follows template <paramaters> class declaration; 2.

How do you inherit a template class in C++?

Class Template Inheritance in C++ If we need the new derived class to be general, we must make it a template class with a template argument sent to the base class. This is because inheritance is only possible with a class, and a template is not a class unless it is instantiated by passing some data type to it.

Can template be used for class?

A template is not a class or a function. A template is a “pattern” that the compiler uses to generate a family of classes or functions. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to “fill in” the template.

How do you create a object of template class?

Class Template DeclarationA class template starts with the keyword template followed by template parameter(s) inside <> which is followed by the class declaration. template <class T> class className { private: T var; ... .. ... public: T functionName(T arg); ... .. ... };


4 Answers

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

like image 132
Jarod42 Avatar answered Oct 18 '22 05:10

Jarod42


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>> );
like image 20
goneskiing Avatar answered Oct 18 '22 05:10

goneskiing


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

like image 1
jenkas Avatar answered Oct 18 '22 05:10

jenkas


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));
};
like image 1
Anton Dyachenko Avatar answered Oct 18 '22 07:10

Anton Dyachenko