Question:
Is there a way in C++ to determine if a pointer type is an inheritor of a particular base type? For instance:
template<class T>
void Foo(T *obj)
{
if (Extends<Object>(obj))
...
else
...
}
Answer:
C++11 introduced the is_base_of
class template (in the type_traits
file):
template< class Base, class Derived >
struct is_base_of;
This class has a static bool
member named value
. It is set to true
if Derived
is a derived type from Base
(whether it's public
, protected
or private
inheritance). And false
otherwise. Also note that value
is set to true
if Base
and Derived
are the same type (doesn't work for fundamental types and pointers).
Here is an example code:
#include <iostream>
#include <type_traits>
using namespace std;
class A {};
class B : public A {};
class C : protected A {};
class D : private A {};
class F : public D {};
class Z {};
int main()
{
cout << boolalpha;
cout << is_base_of<A, B>::value << endl;
cout << is_base_of<A, C>::value << endl;
cout << is_base_of<A, D>::value << endl;
cout << is_base_of<A, A>::value << endl;
cout << is_base_of<A, F>::value << endl;
cout << is_base_of<A, Z>::value << endl;
return 0;
}
And we get the output:
true
true
true
true
true
false
C++17 introduces an auxiliary variable template:
template< class Base, class Derived >
inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
It helps to avoid accessing the value
member. With it, you can write like this:
cout << is_base_of_v<A, B> << endl;
cout << is_base_of_v<A, C> << endl;
cout << is_base_of_v<A, D> << endl;
cout << is_base_of_v<A, A> << endl;
cout << is_base_of_v<A, F> << endl;
cout << is_base_of_v<A, Z> << endl;
And we get the same output as before.
More details can be found here: https://en.cppreference.com/w/cpp/types/is_base_of
C++11 introduced the is_convertible
class template (in the type_traits
file):
template< class From, class To >
struct is_convertible;
It checks if the From
type can be implicitly converted to the To
type. This class, like is_base_of
, has a static constant member value
of type bool
. It is set to true
if the From
type can be implicitly converted to the To
type, and false
otherwise.
Code example:
#include <iostream>
#include <type_traits>
using namespace std;
class A {};
class B : public A {};
class C : protected A {};
class D : private A {};
class X
{
public:
operator A() { return a; }
private:
A a;
};
class Y
{
public:
explicit operator A() { return a; }
private:
A a;
};
int main()
{
cout << boolalpha;
cout << is_convertible<A*, B*>::value << endl;
cout << is_convertible<B*, A*>::value << endl;
cout << is_convertible<C*, A*>::value << endl;
cout << is_convertible<D*, A*>::value << endl;
cout << is_convertible<X, A>::value << endl;
cout << is_convertible<Y, A>::value << endl;
return 0;
}
And output:
false
true
false
false
true
false
No explanation.
is_convertible<A*, B*>::value
evaluates to false
, because no down-converting is allowed in C++.
is_convertible<B*, A*>::value
is true
because implicit up-conversion is only allowed for public inheritance. For this reason, is_convertible<C*, A*>::value
and is_convertible<D*, A*>::value
yield false
.
is_convertible<X, A>::value
is true
because class X
has a conversion operator to A
that can be called implicitly. It is not declared as explicit
.
is_convertible<Y, A>::value
evaluates to false
because its conversion operator to A
is declared explicit
, and cannot be called implicitly.
C++17 introduces an auxiliary variable template:
template< class From, class To >
inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
It helps to avoid accessing the value
member. With it, you can write like this:
cout << is_convertible_v<A*, B*> << endl;
cout << is_convertible_v<B*, A*> << endl;
cout << is_convertible_v<C*, A*> << endl;
cout << is_convertible_v<D*, A*> << endl;
cout << is_convertible_v<X, A> << endl;
cout << is_convertible_v<Y, A> << endl;
We get the same conclusion as before.
More details here: https://en.cppreference.com/w/cpp/types/is_convertible
In C++20, the derived_from
concept should appear in the concepts
file:
template< class Derived, class Base >
concept derived_from =
std::is_base_of_v<Base, Derived> &&
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
More details here: https://en.cppreference.com/w/cpp/concepts/derived_from