Please consider the following code:
class Abase{};
class A1:public Abase{};
class A2:public A1{};
//etc
class Bbase{
public:
virtual void f(Abase* a);
virtual void f(A1* a);
virtual void f(A2* a);
};
class B1:public Bbase{
public:
void f(A1* a);
};
class B2:public Bbase{
public:
void f(A2* a);
};
int main(){
A1* a1=new A1();
A2* a2=new A2();
Bbase* b1=new B1();
Bbase* b2=new B2();
b1->f(a1); // calls B1::f(A1*), ok
b2->f(a2); // calls B2::f(A2*), ok
b2->f(a1); // calls Bbase::f(A1*), ok
b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)!
}
I'm interested to know why C++ chooses to resolve the function call on the last line by upcasting the this
pointer of the object to the base class, rather than upcasting the argument of f()
? Is there any way that I can get the behaviour I want?
The choice of which version of
f
to call is made by looking at the compile-time type of the parameter. The run-time type isn't considered for this name resolution. Sinceb1
is of typeBbase*
, all ofBbase
's members are considered; the one that takes anA2*
is the best match, so that's the one that gets called.It's called name hiding. Every f you declare in one derived class shadows every possible f in any of its base classes.Use a cast to the base class to get the desired behaviour.
When you override a virtual function, you don't override overloaded functions with the same name. They are different functions (and have different entries in the vtable).
This should force the compiler to use the overload method with parameter of type A1.
Your overloads in Bbase for Abase and A2 are hidden in B1. Maybe you can work around that problem like this:
or with a template in Bbase:
"...chooses to resolve the function call on the last line by upcasting the this pointer of the object to the base class...". What are you talking about? In all of your calls, the object pointer type is
Bbase *
and the functions the calls resolve to belong to eitherBbase
or its descendants. The compiler never does any upcasting in order to resolve your calls. In fact, the first two calls require downcasting in order to call the proper overrider, since the overrider belongs to the class located further down in the hierarchy. As for the last two calls - they are dispatched into theBbase
class through a pointer ofBbase *
type. The types match exactly, no casting of any kind takes place.As for the overload resolution... Overload resolution is a compile time process, which is based on the static types of the arguments and the ranks of possible conversions. You supplied an argument of
A2 *
type. Thef(A2 *)
candidate matched your argument precisely. Thef(A1 *)
candidate requires a extra conversion fromA2 *
toA1 *
. The candidate that matches exactly is considered a better one, so it wins the overload resolution. Simple.