Overloaded virtual function call resolution - c++

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. 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.

"...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.

b1->f(static_cast<A1*>(a2));
This should force the compiler to use the overload method with parameter of type A1.

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).

Your overloads in Bbase for Abase and A2 are hidden in B1.
Maybe you can work around that problem like this:
class Bbase{
public:
inline void f(Abase* a) { f_(a); }
inline void f(A1* a) { f_(a); }
inline void f(A2* a) { f_(a); }
protected:
virtual void f_(Abase* a);
virtual void f_(A1* a);
virtual void f_(A2* a);
};
class B1:public Bbase{
protected:
void f_(A1* a);
};
class B2:public Bbase{
protected:
void f_(A2* a);
};
or with a template in Bbase:
class Bbase{
public:
template<class myA>
inline void f(myA* a) { f_(a); }
protected:
virtual void f_(Abase* a);
virtual void f_(A1* a);
virtual void f_(A2* a);
};

Related

Cast derived virtual override to base pure virtual member

I understand why you cannot simply cast a derived class member function pointer to base class member function pointer as explained here.
But, given this snippet:
struct base
{
virtual void foo() = 0;
};
struct derived : base
{
void foo() override {};
};
struct invoker
{
typedef void(base::*target)();
invoker(base* b, target t)
{
(b->*t)();
}
};
template<typename B, typename D>
void (B::*cast(void (D::*method)()))()
{
return static_cast<void(B::*)()>(method);
}
derived d;
invoker bad(&d, &derived::foo); //C2664
invoker good(&d, cast<base>(&derived::foo));
I wanted to ask is it possible to decorate the base function signature so that compiler understands it is a pure virtual method and and it will be implemented somewhere across the hierarchy (otherwise I could not construct an object of type B)? I understand why I can't do this with normal functions, but IMHO in case of a pure virtual function the compiler has a guarantee it will be implemented (in case it was not done I would get an error about the class B not about the cast).
There's no need to manipulate the type of &derived::foo. One can just use &base::foo instead.
Pointers to member functions respect virtuality. This call
base* pBase = new derived;
auto pFoo = &base::foo;
(pBase->*pFoo)();
will actually call derived::foo, exactly like a simple call pBase->foo() would.
Even if there's a guarantee that it's implemented, it might use additional data members declared only in derived. A possible solution is to use function pointers and pass this as the first parameter (this also shows you why you cannot do it via virtual).
Consider the following diamond hierarchy:
struct base {
virtual void foo() = 0;
};
struct D1 : public virtual base {
virtual void foo() override;
};
struct D2 : public virtual base {
virtual void foo() override;
};
struct Derived : public virtual D1, D2 {
virtual void foo() final;
};
Now consider a scenario where a upcast is allowed from Derived::* to base::* . Which function should be invoked? The compiler loses information about which of D1::foo, D2::foo or Derived::foo you wish to call since that information has been cast away. To avoid this sort of ambiguity such an upcast is disallowed.

Virtual functions and default parameters

Lets say I have a class A, B and C
class A{
public:
virtual void f4(){
cerr<<"A::f4()"<<endl;
}
};
class B: public A{
public:
virtual void f4(int n){
cerr<<"B::f4("<<n<<")"<<endl;
}
};
class C: public B{
public:
virtual void f4(int n = 1){
cerr<<"C::f4("<<n<<")"<<endl;
}
};
If I have:
C c;
A& rac = c;
rac.f4();
I was expecting c's version of f4 to be called but that's not what happens. Can someone explain?
Compiling with clang -Wall the following warnings occur:
main.cpp:14:22: warning: 'B::f4' hides overloaded virtual function [-Woverloaded-virtual]
virtual void f4(int n){
^
main.cpp:6:22: note: hidden overloaded virtual function 'A::f4' declared here: different number of parameters (0 vs 1)
virtual void f4(){
^
These warnings explain what's going on. A virtual function is only overridden by another function with the same signature.
B::f4(int) does not override A::f4() because they have different parameter lists. Instead, it is a different virtual function.
So rac.f4() just calls A::f4() which is not overridden.
Since C++11 you can help to detect this problem:
virtual void f4(int n) override {
// ^^^^^^^^
Then the compiler will give an error if this function does not actually override something from the base.
Because the signatures of f4 don't match between A and B/C classes.
This is A's function signature for f4:
void A::f4() // no parameters
B and C use this function signature
void B::f4(int n)
If you are doing to override a method, it needs to have the same return type and identical parameter list. Otherwise, even if the method in the derived class has the same name as the parent class, C++ will don't consider that an override.
FWIW, C::f4 does override B::f4. The default parameter you introduce on class C doesn't influence the virtual methods override behavior. And this is the exact reason why, on my team, we disallow overloading methods with different signatures and banning default parameters. Because it leads to bugs like this.
Here's C's v-table (the methods available on it):
void C::f4(); // no parameters, overrides A::f4()
void C::f4(int n); // overrides B::f4()

Difference between overriding and overloading of a function in C++ [duplicate]

This question already has answers here:
Differentiate between function overloading and function overriding
(11 answers)
Overloading and overriding
(12 answers)
Closed 7 years ago.
I am studying c plus plus course in my college and i am unable to differentiate between overriding and overloading of a function can anyone please help me.
Here are two different overloads of foo:
void foo(int);
void foo(char);
Here B::bar is a function override:
class A {
public:
virtual void bar();
};
class B : public A {
public:
void bar() override;
};
overloading means functions with same name having different parameters , it does not really depend whether you are using procedural language or object oriented language you can do overloading. well as far as over riding is concerned it means we are explicitly defining a function that exist in base class in derived class . obviously you need object oriented language to perform over riding as it is done between base and derived classes.
Overloading means declaring more than one function with the same name in the same scope. They must have different parameter types, and the suitable overload is chosen at compile time based on the arguments' static types.
void f(int);
void f(double);
f(42); // selects the "int" overload
f(42.0); // selects the "double" overload
Overriding means that a derived class declares a function that matches a virtual function declared in the base class. Calling the function via a pointer or reference to the base class will select the override at run-time, based on the object's dynamic type.
struct Base {
virtual void f();
};
struct Derived : Base {
void f() override; // overrides Base::f
};
Base * b = new Base; // dynamic type is Base
Base * d = new Derived; // dynamic type is Derived
b->f(); // selects Base::f
d->f(); // selects Derived::f
Overriding means, giving a different definition of an existing function with same parameters,
and overloading means adding a different definition of an existing function with different parameters.
Example:
#include <iostream>
class base{
public:
virtual void show(){std::cout<<"I am base";} //this needs to be virtual to be overridden in derived class
void show(int x){std::cout<<"\nI am overloaded";} //this is overloaded function of the previous one
};
class derived:public base{
public:
void show(){std::cout<<"I am derived";} //the base version of this function is being overridden
};
int main(){
base* b;
derived d;
b=&d;
b->show(); //this will call the derived version
b->show(6); // this will call the base overloaded function
}
Output:
I am derived
I am overloaded

Understanding Inheritance and functions with different signatures

Say I have a base class and a derived class.
class Base {
public:
void A(int x, int y) {do something}
void B() {
A(x,y);
do something;
}
};
class Derived : public Base {
void A() {do something else};
};
Derived derived1;
derived1.B();
The signatures of the functions are different, will the B call the derived A or the base A? Iif it will call the derived B, I guess it will dismiss the parameters?
what if the derived A needed different parameters rather than no parameters, would I have to copy B's entire code into the derived class just to change the way B calls A?
A function's code is always evaluated in the context of the class in which it is defined. This includes determining which function each expression calls. So inside Base::B(), the call A(x, y) is translated by the compiler as a call to Base::A. Even if you later call derived1.B(), it will call derived1 . Base::A (pseudo-syntax).
The only thing which changes this slightly is virtual functions. However, even with them, the rules are similar. Overload resolution (which is basically the process of matching a function name & signature to a call expression) is done in the context of the class where the containing function is defined. If the resolution leads to a virtual function being selected, the virtual call mechanism will then be invoked at runtime to call the appropriate override of that function.
Let's consider this example:
struct Base {
virtual void foo(int);
virtual void bar() { foo(0.0); }
};
struct Derived : Base {
virtual void foo(int);
virtual void foo(double);
};
Derived d;
d.bar();
Even in this example, calling d.bar() will call Derived::foo(int). That's because the call-to-signature matching was done in the context of Base, which only sees foo(int) and employs the implicit conversion from double to int.
Two reasons force Base::B to call Base::A not Derived::A
It's calling A with specific overload: A(int, int)
Base::A and Base::B are not virtual, so it calls Base::A. The code is not polymorphic.
The simplest example to show a how a virtual method works is:
class Base {
public:
virtual void A() {
// do job #1
}
};
class Derived : public Base {
public:
virtual void A() {
// do job #2
}
};
// ...
Derived derived1;
Base *base = &derived1;
base->A(); // <---- It calls `Derived::A()` and does job #2
But, if you write A in B with different parameter (overload it), you have to call it explicitly with actual arguments.

Virtual method causes compilation error in Derived class

Consider the next code :
#include <iostream>
using namespace std;
class A
{
public:
virtual int f() {cout <<"A:: f()\n"; return 1;}
virtual void f(int) {cout <<"A:: f(int)\n";}
virtual void g() {cout <<"A::g()\n";}
};
class B3 : public A
{
public:
void f() {cout <<"B3::f ()\n";}
};
int main()
{
return 0;
}
It produces the following error :
..\main.cpp:17: error: conflicting return type specified for 'virtual void B3::f()'
..\main.cpp:9: error: overriding 'virtual int A::f()'
but why ? in the worst case I'd think I'd have an Hiding case , but instead I get compilation error regarding A's virtual int f() {cout <<"A:: f()\n"; return 1;}
thanks ,Ronen
Don't confuse overriding with hiding. You override virtuals.
Your class definition is equivalent to:
class B3 : public A
{
public:
virtual void f() {cout <<"B3::f ()\n";}
};
Once a function declared virtual, it will remain virtual for all classes deriving from your base class, regardless of whether you explicitly declare it virtual or not. Therefore you are trying to override a virtual function and changing its return type, which is illegal in C++. If the function were not virtual, you would simply be hiding the base class implementation, therefore changing the return type is valid. There would be no ambiguity since the compiler would know where to call the function from and what return type to expect.
However, consider having:
A* a;
....
a->f();
What would a-f() return? a is a pointer to A, but can point to an object of type B3. So it either returns an int or doesn't return anything. See the ambiguity here?
Instead, no polymorphism involved,
A a;
a.f();
will cal f from a, same as b3.f would call f from B3.
All in all, overriding base class functions implies keeping the same return type. If you want to create a new function with a different return type, change its signature (either its name or its parameters - or both).
Anyway, you shouldn't even be doing this... Why would you want to have a function with the same name and no parameters return different things? Wouldn't adding a separate function be more readable?
You'd have hiding if f() would have had a different parameter list, or not declared as virtual on the base class. In the former case, since overloading doesn't cross inheritance borders, A's f would have been hidden. But this is not the case, since you have f() on both classes, which only differ in return value. Return values covariance is the only difference allowed, and since it's not the case (void does not inherit from int), you get the error.