iammilind
iammilind

Reputation: 69998

Alternate implementation of "is_base_of" (checking base/derived relationship)

I was going through a nice question for how is_base_of is implemented in boost (which decides if a given class is a base of another class at compile time].

Seeing such code for the 1st time, I got amazed that how things can be made to work so nicely! However, many steps I got confused to understand (after reading all the answers). So, I was wondering that if this functionality can be implemented alternatively. I tried following:

template<class B, class D>
struct is_base_of
{
  template<typename T> struct dummy {};
  struct Child : D, dummy<int> {};

  static B* Check (B*);
  template<class T> static char Check (dummy<T>*);

  static const bool value = (sizeof(Check((Child*)0)) == sizeof(B*));
};

It works fine as per expectation for the general case.

The only problem is for private/protected inheritance. It goes and choose the expected function, but also shows error as:- is an inaccessible base of .... I would appreciate if someone can suggest any little modification missing to the code to solve this problem (If not the functionality wise then at least to get rid of the error message).

[Note: I have assume that char and B* will always be of different sizes and avoided typecasting of typical 'yes' and 'no']

Upvotes: 3

Views: 2778

Answers (1)

julx
julx

Reputation: 9091

I think the key part in the linked solution was that no matter what the end result would be (related or unrelated) - the chosen conversion sequence wouldn't actually include the inheritance that is tested for. Compiler is taking it into consideration in the process of selecting the appropriate version of the check function. However each time in the end the path selected wouldn't actually use it.

In your code however if the classes are related, the call to check indeed does make use of the inheritence converting from Child* to B*. And this - I'm afraid cannot be fixed easily as the approach that you presented is very different.

If related

Your solution

Child is D therefore is also B. Therefore there exists conversion from Child* to B*. So the first version of Check is viable. The second version of Check is viable as well as Child is also dummy<int> and therefore Child* is dummy<int>*. The first version is selected as this doesn't involve template parameter specialization.

The linked solution

For the first version of check: Host<B, D> is converted via user defined conversion to D*. The conversion result type exactly matches the function argument. No inheritance is used here.

For the second version of check: Host<B, D> is converted again via user defined conversion to D*. The conversion result type is D* which can be further converted to B* in order to match the function argument. Inheritance is indeed used here.

However in the end compiler chooses the first version as it was noted the conversion result better matches the argument of check.

If unrelated

Your solution

Child isn't B therefore the first version of Check is not an option. Second version is viable as the Child is still dummy<int> therefore can be specialized from dummy<T>. As there is only one option the choice is trivial.

The linked solution

For the first version of check: Host<B, D> is converted to D* via user defined conversion.

For the second version of check: Host<B, D> is converted to const Host<B, D> and then to B* via user defined conversion.

The paths would be incomparable if it were not for the fact that only one version of check function has template arguments.

As you can see the two approaches are significantly different.

Upvotes: 2

Related Questions