Return type of "this" in virtual functions

176 Views Asked by At

In the code I'm giving you, there is E that derives from C, and I have a pointer to an object of C.

#include <iostream>
using namespace std;

class C {
  public: virtual C* f(){ cout << "C::f()" << endl; return this; }
};

class E: public C {
  public: E* f(){ cout << "E::f()" << endl; return this; }
};

int main(){
  C* pc = new E;
  auto p = pc->f();
  cout << typeid(p).name() << endl;
}

When I call pc->f(), it goes to E::f() due to the virtual function, and I get it, but what is the return type of return this;?

Because this is a C*, but in the signature the method should return an E*.

If you run it, it prints:

E::f()
P1C
2

There are 2 best solutions below

3
Caleth On BEST ANSWER

The type of p is C*, but the object it points to is an E.

If you print the typename of *p, you get (mangled) E.

cout << typeid(p).name() << endl << typeid(*p).name() << endl;

See it on coliru

When you call f through an E*, you get an E* back.

int main(){
  E* pe = new E;
  C* pc = pe;
  auto p1 = pe->f();
  auto p2 = pc->f();
  cout << typeid(E).name() << endl << typeid(C).name() << endl;
  cout << typeid(p1).name() << endl << typeid(p2).name() << endl;
  cout << typeid(*p1).name() << endl << typeid(*p2).name() << endl;
}

More coliru

5
463035818_is_not_an_ai On

Here:

class E: public C{
  public: E* f(){cout << "E::f()" << endl; return this;}
};

a pointer to E* is returned. this is pointer to the current object, an E.

Because this is a C* but in the signature the method should return an E*.

No, this is a pointer to E* and E* can be converted to C*. It is called "covariant return type". In a nutshell: It is ok for E::f to return an E* even though it overrides C::f which returns a C*. That is because E inherits publicly from C and E* can be converted to C*.

For a caller that expects the signature as declared in C there is no issue:

 void foo(C& c) {
     C* p = c.f();  // OK!   
 }

But then why typeid tells it's a C*?

First of all you need to take care about the names. They are arbitrary. Looking only at the output of a single type name cannot tell with certainty what type it is.

Then you should do this to get the full picture:

cout << typeid(C).name() << endl;
cout << typeid(E).name() << endl; 
cout << typeid(p).name() << endl;
cout << typeid(*p).name() << endl;

Live Demo:

1C
1E
P1C
1E

p is a C*, thats the signature of C::f the virtual function you called, but the object it points to is an E.