Consider the code :
#include <stdio.h>
class Base {
public:
virtual void gogo(int a){
printf(" Base :: gogo (int) \n");
};
virtual void gogo(int* a){
printf(" Base :: gogo (int*) \n");
};
};
class Derived : public Base{
public:
virtual void gogo(int* a){
printf(" Derived :: gogo (int*) \n");
};
};
int main(){
Derived obj;
obj.gogo(7);
}
Got this error :
>g++ -pedantic -Os test.cpp -o test test.cpp: In function `int main()': test.cpp:31: error: no matching function for call to `Derived::gogo(int)' test.cpp:21: note: candidates are: virtual void Derived::gogo(int*) test.cpp:33:2: warning: no newline at end of file >Exit code: 1
Here, the Derived class's function is eclipsing all functions of same name (not signature) in the base class. Somehow, this behaviour of C++ does not look OK. Not polymorphic.
This is "By Design". In C++ overload resolution for this type of method works like the following.
Since Derived does not have a matching function named "gogo", overload resolution fails.
Name hiding makes sense because it prevents ambiguities in name resolution.
Consider this code:
If
Base::func(float)
was not hidden byDerived::func(double)
in Derived, we would call the base class function when callingdobj.func(0.f)
, even though a float can be promoted to a double.Reference: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/
Judging by the wording of your question (you used the word "hide"), you already know what is going on here. The phenomenon is called "name hiding". For some reason, every time someone asks a question about why name hiding happens, people who respond either say that this called "name hiding" and explain how it works (which you probably already know), or explain how to override it (which you never asked about), but nobody seems to care to address the actual "why" question.
The decision, the rationale behind the name hiding, i.e. why it actually was designed into C++, is to avoid certain counterintuitive, unforeseen and potentially dangerous behavior that might take place if the inherited set of overloaded functions were allowed to mix with the current set of overloads in the given class. You probably know that in C++ overload resolution works by choosing the best function from the set of candidates. This is done by matching the types of arguments to the types of parameters. The matching rules could be complicated at times, and often lead to results that might be perceived as illogical by an unprepared user. Adding new functions to a set of previously existing ones might result in a rather drastic shift in overload resolution results.
For example, let's say the base class
B
has a member functionfoo
that takes a parameter of typevoid *
, and all calls tofoo(NULL)
are resolved toB::foo(void *)
. Let's say there's no name hiding and thisB::foo(void *)
is visible in many different classes descending fromB
. However, let's say in some [indirect, remote] descendantD
of classB
a functionfoo(int)
is defined. Now, without name hidingD
has bothfoo(void *)
andfoo(int)
visible and participating in overload resolution. Which function will the calls tofoo(NULL)
resolve to, if made through an object of typeD
? They will resolve toD::foo(int)
, sinceint
is a better match for integral zero (i.e.NULL
) than any pointer type. So, throughout the hierarchy calls tofoo(NULL)
resolve to one function, while inD
(and under) they suddenly resolve to another.Another example is given in The Design and Evolution of C++, page 77:
Without this rule, b's state would be partially updated, leading to slicing.
This behavior was deemed undesirable when the language was designed. As a better approach, it was decided to follow the "name hiding" specification, meaning that each class starts with a "clean sheet" with respect to each method name it declares. In order to override this behavior, an explicit action is required from the user: originally a redeclaration of inherited method(s) (currently deprecated), now an explicit use of using-declaration.
As you correctly observed in your original post (I'm referring to the "Not polymorphic" remark), this behavior might be seen as a violation of IS-A relationsip between the classes. This is true, but apparently back then it was decided that in the end name hiding would prove to be a lesser evil.
The name resolution rules say that name lookup stops in the first scope in which a matching name is found. At that point, the overload resolution rules kick in to find the best match of available functions.
In this case,
gogo(int*)
is found (alone) in the Derived class scope, and as there's no standard conversion from int to int*, the lookup fails.The solution is to bring the Base declarations in via a using declaration in the Derived class:
...would allow the name lookup rules to find all candidates and thus the overload resolution would proceed as you expected.