Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a class explicitely defines a member type in an inheritance hierarchy

I have a set of classes that are chained using a member typedef Next, as follows:

class Y; class Z;
class X { public: typedef Y Next; };
class Y { public: typedef Z Next; };
class Z { };

I need a way to get the final class of a chain, starting from any class of the chain. Thanks to the accepted answer of this post, I wrote the following code:

// cond_type<Condition, Then, Else>::type   // selects type 'Then' if 'Condition' is true, or type 'Else' otherwise
template <bool Condition, typename Then, typename Else = void>
struct cond_type
{
    typedef Then type;
};
template <typename Then, typename Else>
struct cond_type<false, Then, Else >
{
    typedef Else type;
};

template <class C, typename _ = void> 
struct chain
{
    typedef C last; 
};
template <class C>
struct chain<C, typename cond_type<false, typename C::Next>::type>
{
    typedef typename chain<typename C::Next>::last last;
};

Using the above template chain<C>::last, the following code properly instantiates 3 objects of class Z, as expected:

chain<X>::last z1;
chain<Y>::last z2;
chain<Z>::last z3;

However, if the considered set of classes form an inheritance hierarchy, in the following way:

class U; class V;
class T             { public: typedef U Next; };
class U : public T  { public: typedef V Next; };
class V : public U  { };

Then, using template chain<C>::last, with any class C of the above set, for example:

chain<T>::last v;

result in the following compile error:

1>test.cpp(88): error C3646: 'last' : unknown override specifier

I understand that the problem is that class V inherits from typedef V Next defined in the parent class U, resulting in compilation of the specialized form of the template chain<V,V> while the generic one should be used instead as V has no member Next.

Anyway, I am stuck here, as I need a mechanism that works, even in this case of a class hierarchy.

How could I do this ?

PS: inheritance between classes must remain public; member typedefs must remain public.

like image 485
shrike Avatar asked Jan 07 '18 11:01

shrike


People also ask

What is class inheritance hierarchy?

In the inheritance hierarchy, classes become more specific and concrete with each new subclass. If you move from a subclass back up to a superclass, the classes become more general and less specific. Class design should ensure that a superclass contains common features of its subclasses.

How many types of classes in inheritance hierarchy includes?

OOPs support the six different types of inheritance as given below : Single inheritance. Multi-level inheritance.

What is inheritance hierarchy in Java?

The type of inheritance in which more than one derived class inherits the properties of the same base class is called hierarchical inheritance. There are multiple child classes and a single parent class.

How do you check if an object belongs to a certain class in C++?

C++ has no direct method to check one object is an instance of some class type or not. In Java, we can get this kind of facility. In C++11, we can find one item called is_base_of<Base, T>. This will check if the given class is a base of the given object or not.


1 Answers

It's as simple as:

template <typename T, typename = void> struct last_t_impl
{
    using type = T;
};
template <typename T> struct last_t_impl
    <T, std::enable_if_t<!std::is_same_v<typename T::Next, T>>>
{
    using type = typename last_t_impl<typename T::Next>::type;
};
template <typename T> using last_t = typename last_t_impl<T>::type;

Usage:

last_t<T> v1;
last_t<U> v2;
last_t<V> v3;

If you need the code above to compile for C++14 (instead of C++17), change std::is_same_v<A,B> to std::is_same<A,B>::value.


Note that your typename cond_type<false, T>::type can be replaced with std::void_t<T> (or std::conditional_t<false,T,void> in C++14). But in this case it's not needed, since an end of a chain will be SFINAE-detected by std::is_same_v<typename T::Next, T>. (Even if T::Next doesn't exist for some reason, the SFINAE will still kick in and last_t<T> will be just T.)

like image 166
HolyBlackCat Avatar answered Sep 27 '22 21:09

HolyBlackCat