Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking for template parent class in C++ using SFINAE

I've been learning the concept of SFINAE in C++ recentlly and I am currentlly trying to use it in a project.

The thing is, what I'm trying to do is different than anything I could find, and I can't figure out how to do it.

Let's say I have a template class called MyParent:

template <typename Elem>
class MyParent;

And a non-template class called MyClass, that inherites it, using char as Elem:

class MyClass : public MyParent<char>;

Now, I want to use SFINAE in order to check if a typename inherites MyParent, regardless of what Elem type is used.

I can't use std::is_base_of, because of the parent's template.

I've tried to do the following:

template <typename T>
struct is_my_parent : std::false_type {};
template <typename Elem>
struct is_my_parent<MyParent<Elem>> : std::true_type {};

Now, if I check for is_my_parent<MyParent<Elem>>::value, it gives me true. Which is good. However, when I check for is_my_parent<MyClass>::value, I recive false. Which kind of makes sence because MyClass isn't actually MyParent<Elem>, but I didn't manage to get what I wanted.

Is there any convenient way to achive such a thing in C++, other than defining is_my_parent for each and every class that inherites from MyParent?

like image 327
aviad1 Avatar asked Dec 23 '22 15:12

aviad1


2 Answers

You might do

template <typename T>
std::true_type is_my_parent_impl(const MyParent<T>*);

std::false_type is_my_parent_impl(const void*);

template <typename T>
using is_my_parent = decltype(is_my_parent_impl(std::declval<T*>()));

Demo

like image 143
Jarod42 Avatar answered Feb 12 '23 22:02

Jarod42


Is there any convenient way to achive such a thing in C++, other than defining is_my_parent for each and every class that inherites from MyParent?

There is, but you'll need to use more elaborate meta-programming techniques. Go entirely back to basics, as it were.

template <class C>
class is_my_parent {
    using yes = char;
    using no  = char[2];
    
    template<typename t>
    static yes& check(MyParent<t> const*);

    static no& check(...);

public:
    enum { value = (1 == sizeof check(static_cast<C*>(0))) };
};

It relies on two basic properties of function overloading and templates:

  1. A derived class can be used to match a function template that takes a base class template as an argument.
  2. Ellipsis offer a conversion sequence that is always considered worse than any other.

Then it's just a matter of inspecting the return type of the chosen overload to determine what we got. Other than the type alias, you can even use this in C++03. Or you can modernize it, so long as overload resolution does the work for you, the check will be performed just the same.

like image 44
StoryTeller - Unslander Monica Avatar answered Feb 12 '23 21:02

StoryTeller - Unslander Monica