How does `is_base_of` work?

If they are related

Let’s for a moment assume that B is actually a base of D. Then for the call to check, both versions are viable because Host can be converted to D* and B*. It’s a user defined conversion sequence as described by 13.3.3.1.2 from Host<B, D> to D* and B* respectively. For finding conversion functions that can convert the class, the following candidate functions are synthesized for the first check function according to 13.3.1.5/1

D* (Host<B, D>&)

The first conversion function isn’t a candidate, because B* can’t be converted to D*.

For the second function, the following candidates exist:

B* (Host<B, D> const&)
D* (Host<B, D>&)

Those are the two conversion function candidates that take the host object. The first takes it by const reference, and the second doesn’t. Thus the second is a better match for the non-const *this object (the implied object argument) by 13.3.3.2/3b1sb4 and is used to convert to B* for the second check function.

If you would remove the const, we would have the following candidates

B* (Host<B, D>&)
D* (Host<B, D>&)

This would mean that we can’t select by constness anymore. In an ordinary overload resolution scenario, the call would now be ambiguous because normally the return type won’t participate in overload resolution. For conversion functions, however, there is a backdoor. If two conversion functions are equally good, then the return type of them decides who is best according to 13.3.3/1. Thus, if you would remove the const, then the first would be taken, because B* converts better to B* than D* to B*.

Now what user defined conversion sequence is better? The one for the second or the first check function? The rule is that user defined conversion sequences can only be compared if they use the same conversion function or constructor according to 13.3.3.2/3b2. This is exactly the case here: Both use the second conversion function. Notice that thus the const is important because it forces the compiler to take the second conversion function.

Since we can compare them – which one is better? The rule is that the better conversion from the return type of the conversion function to the destination type wins (again by 13.3.3.2/3b2). In this case, D* converts better to D* than to B*. Thus the first function is selected and we recognize the inheritance!

Notice that since we never needed to actually convert to a base class, we can thereby recognize private inheritance because whether we can convert from a D* to a B* isn’t dependent on the form of inheritance according to 4.10/3

If they are not related

Now let’s assume they are not related by inheritance. Thus for the first function we have the following candidates

D* (Host<B, D>&) 

And for the second we now have another set

B* (Host<B, D> const&)

Since we cannot convert D* to B* if we haven’t got a inheritance relationship, we now have no common conversion function among the two user defined conversion sequences! Thus, we would be ambiguous if not for the fact that the first function is a template. Templates are second choice when there is a non-template function that is equally good according to 13.3.3/1. Thus, we select the non-template function (second one) and we recognize that there is no inheritance between B and D!

Leave a Comment