Here we have is_base_of template implementation taken from cppreference.com:
namespace details {
template <typename B>
std::true_type test_pre_ptr_convertible(const B*); //1
template <typename>
std::false_type test_pre_ptr_convertible(const void*); //2
template <typename, typename>
auto test_pre_is_base_of(...)->std::true_type; //3
template <typename B, typename D>
auto test_pre_is_base_of(int) ->
decltype(test_pre_ptr_convertible<B>(static_cast<D*>(nullptr)));
}
template <typename Base, typename Derived>
struct is_base_of :
std::integral_constant<
bool,
std::is_class<Base>::value&& std::is_class<Derived>::value&&
decltype(details::test_pre_is_base_of<Base, Derived>(0))::value
> { };
And some private inheritance:
class A {};
class B : A {};
is_base_of<A,B>::value gives true and declaration no. 3 is the best match. Declaration no. 1 fails as a candidate(was passed a pointer to object of private subclass) and declaration no. 2 is ignored. But Why? Isn't void* a good match for every pointer type? If declaration no. 3 was not provided the code wouldn't compile. My question is why declaration no. 3 needs to be provided for this code to compile successfully? Why declarations no. 1 and no. 2 aren't sufficient?
Overload resolution ignores accessibility for the purpose of deciding which candidates are viable and for choosing the best candidate.
If
Dis a derived class ofB(irregardless of accessibility), then//1is always viable and a better candidate for overload resolution than//2(which is also viable) and overload resolution will choose the former.If
//1is chosen as best candidate andBis not a public base ofD, then the required implicit conversion on the argument will however fail becauseBis not an accessible base. But that is already after overload resolution was done. There will be no attempt to then fall back to the "second best" overload//2.Since the call to
test_pre_ptr_convertibleis therefore invalid, the whole program would normally be ill-formed. However, the call here is in the return type of a function template and so SFINAE applies, meaning that the overloadis simply not viable for the call from
is_base_ofand the only other remaining overload fortest_pre_is_base_ofis//3, which is then chosen.