Below is the code i have come across that actually does the job of std::is_base_of
function in C++11 or boost::is_base_of
.
Below helper function does the same:
template<typename D, typename B>
class IsDerivedFromHelper
{
class No { };
class Yes { No no[3]; };
static Yes Test( B* );
static No Test( ... );
public:
enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) };
};
template <class C, class P>
bool IsDerivedFrom() {
return IsDerivedFromHelper<C, P>::Is;
}
Can someone explain what excatly the enum Is
does?
What are static Yes
Test
and static No
Test
? Are they function calls ?
Templates in c++ is defined as a blueprint or formula for creating a generic class or a function. To simply put, you can create a single function or single class to work with different data types using templates. C++ template is also known as generic functions or classes which is a very powerful feature in C++.
A template allows us to create a family of classes or family of functions to handle different data types. Template classes and functions eliminate the code duplication of different data types and thus makes the development easier and faster. Multiple parameters can be used in both class and function template.
Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.
class No { };
- defines a class, which will have an undefined size, which won't be zero (mandated by the standard)
class Yes { No no[3]; };
- defines another class, Yes
which will be at least 3 times as big as a No
. So they are guaranteed to be different sizes.
static Yes Test( B* );
- declare a function which returns a Yes
, but don't give it a definition (we don't need one). It will match any pointer argument which is pointing to an object derived from B
.
static No Test( ... );
- declare a function which returns a No
(smaller, remember?). It's an overload which will be selected if the more specific one (above) cannot be satisfied. It accepts any argument (but the other version will be selected by overload resolution in preference if the argument is derived from B
).
sizeof(Test(static_cast<D*>(0)))
deduces the size of the object returned by Test
when passes a pointer to a D
. If D
is derived from B
, it will be a Yes
, otherwise it will be a No
.
Because the 'call' is made in a non-deduced context, it is not evaluated (called), just selected and return type deduced.
The rest is probably self-explanatory. turns out it wasn't :)
So this all gets put together here:
enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) };
What this is doing in a nutshell, is saying:
"declare a constant called Is
, which will be true
if the result of calling Test with a D*
is a type that happens to be the same size as a Yes
. And it will be false
if the result of the call happens to be a type which is a different size. Because of the two overloads, above, when D is a B, or anything derived from it, Test(D*)
will select the Yes
version, which will return a Yes
, which is obviously the same size as a Yes
. If this overload is not selected, the more permissive (but lower priority) one will be. That one returns a No
, which obviously won't be the same size as a Yes
(by definition)."
Why is Test(...)
'lower priority' than Test(B*)
(in terms of overload resolution)? Because it just is. The standard defines it to be.
Finally:
template <class C, class P>
bool IsDerivedFrom() {
return IsDerivedFromHelper<C, P>::Is;
}
This creates a function which will return the result of the above test for any two class types, C
and P
. This it will return true if P
is derived from a C
and false otherwise.
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