void f(double, double); // at global scope struct Grandparent { void f(int); void f(double, double); }; struct Parent : public Grandparent { void f(int); // hides all overloads of Grandparent::f }; struct Child : public Parent { void g() { f(2.14, 3.17); } // resolves to Parent::f }; In the above example, Child::g contains a reference to the name f. However, the program as a whole contains four declarations of the name f. In order to figure out which f is meant, the compiler computes an
overload set containing all the declarations which are not hidden at the point of the call. The declaration of f at global scope is hidden by Grandparent::f, and in turn Grandparent::f is hidden by Parent::f. Thus the only declaration which is considered by overload resolution is Parent::f — and the result in this case is a diagnostic, because the call-site provides two arguments where Parent::f expects only one. It is often surprising to new C++ programmers that the declaration of Parent::f dominates and hides
all of the more-ancestral declarations, regardless of signature; that is, Parent::f(int) dominates and hides the declaration of Grandparent::f(double, double) even though the two member functions have very different signatures. It is also important to observe that in C++,
name lookup precedes
overload resolution. If Parent::f had multiple overloads (for example f(int) and f(double, double)), the compiler would choose between them at overload-resolution time; but during the name-lookup phase we are concerned only with choosing among the three scopes Grandparent::f, Parent::f, and ::f. The fact that Grandparent::f(double, double) would have been a better
overload than f(int) is not part of the compiler's consideration. ==Example with diamond inheritance==