Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloaded virtual function call resolution

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?

like image 338
stw Avatar asked Jun 23 '10 15:06

stw


2 Answers

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. Since b1 is of type Bbase*, all of Bbase's members are considered; the one that takes an A2* is the best match, so that's the one that gets called.

like image 122
Mark Ransom Avatar answered Oct 09 '22 09:10

Mark Ransom


"...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 either Bbase 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 the Bbase class through a pointer of Bbase * 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. The f(A2 *) candidate matched your argument precisely. The f(A1 *) candidate requires a extra conversion from A2 * to A1 *. The candidate that matches exactly is considered a better one, so it wins the overload resolution. Simple.

like image 31
AnT Avatar answered Oct 09 '22 11:10

AnT