Get derived type via base class virtual function
Solution 1
C++ covariant return types support will only work, as long you already know the derived type. To downcast a base class to a possibly derived class, simply use dynamic_cast<derived>(base_ref)
to determine if base_ref matches the actual derived type:
int main () {
base* pd = new derived();
derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd
// isn't a 'derived'
x.fn();
return 0;
}
Or alternatively:
int main () {
base* pd = new derived();
derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn't
// a 'derived'
if(x) {
x->fn();
}
else {
// dynamic_cast<derived*> failed ...
}
return 0;
}
c++ supports covariant return types for derived classes, but as the other answers describe you cannot get it via calling the base class (pd->get_this()
) here.
You might also consider static polymorphism to check type compliance at compile time, if you can't use RTTI, exception handling or want tight type binding (without vtable overhead).
Solution 2
The static type of pd
is base *
. Thus, when the compiler looks for the member function get_this()
, it finds only base::get_this()
. The return type of base::get_this()
is base&
, which is not convertible to derived&
. Hence the error.
Solution 3
I would like to add to Novelocrat's answer by referring you to section 10.3, paragraph 8 of the working draft C++ standard (click here) which explains in which case the returned pointer's static type is Derived* as opposed to Base*. Basically, if you would have called get_this()
through a pointer to the dervied class then you would have gotten the right type with no compiler error.
Here is a quote from the standard along with an example (also from the standard):
If the return type of D::f differs from the return type of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D. When the overriding function is called as the final overrider of the overridden function, its result is converted to the type returned by the (statically chosen) overridden function (5.2.2). [Example:
class B { };
class D : private B { friend class Derived; };
struct Base {
virtual void vf1();
virtual void vf2();
virtual void vf3();
virtual B* vf4();
virtual B* vf5();
void f();
};
struct No_good : public Base {
D* vf4(); // error: B (base class of D) inaccessible
};
class A;
struct Derived : public Base {
void vf1(); // virtual and overrides Base::vf1()
void vf2(int); // not virtual, hides Base::vf2()
char vf3(); // error: invalid difference in return type only
D* vf4(); // OK: returns pointer to derived class
A* vf5(); // error: returns pointer to incomplete class
void f();
};
void g() {
Derived d;
Base* bp = &d; // standard conversion:
// Derived* to Base*
bp->vf1(); // calls Derived::vf1()
bp->vf2(); // calls Base::vf2()
bp->f(); // calls Base::f() (not virtual)
B* p = bp->vf4(); // calls Derived::pf() and converts the
// result to B*
Derived* dp = &d;
D* q = dp->vf4(); // calls Derived::pf() and does not
// convert the result to B*
dp->vf2(); // ill-formed: argument mismatch
}
Solution 4
C++ supports covariant return type.
What it means is that when you call get_this()
on a derived
object through a base
pointer it is the implementation of derived that is going to be called.
However this does not mean that calling base::get_this
will give you a derived&
. The return type of base::get_this
is base&
. if you want to get a derived
object you will have to call get_this
through a derived
pointer (or downcast your base&
to a derived&
). Note that this is how return type covariance work in Java, C++, D...
base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();
base& a = pbase->get_this(); // call implementation in base, return base&
base& b = pderived->get_this(); // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&
Related videos on Youtube
linuxfever
Updated on August 17, 2020Comments
-
linuxfever almost 4 years
I am trying to get the derived type of an object via a base class virtual function. I have written this, which does not compile:
struct base { virtual base& get_this() { return *this; } }; struct derived : base { virtual derived& get_this() override { return *this; } void fn(); }; int main () { base* pd = new derived(); derived& x = pd->get_this(); /*ERROR*/ x.fn(); return 0; }
... giving me an error that: I cannot initialize a
derived&
from abase
. Sinceget_this
is virtual, why doespd->get_this()
return abase&
instead of aderived&
? Thanks in advance!EDIT:
Thanks everyone for their useful answers and apologies for my late reply. I should have specified in the original post that I am also interested in a solution to my problem rather than just figuring out why the above does not compile. My main problem is that
fn
is unique to thederived
class and cannot be called via the base class. Using casts sure solves the problem but I hate writing code with if else constructs just to get the right type (also Scott Meyers advise against casts :)) . The answers seem to indicate that casts are the way to go, which in a way is at least reassuring that I am not neglecting a more 'elegant' solution to my problem. Thanks again!-
πάντα ῥεῖ over 10 yearsDeleted my answer, but I still believe the OP will be better off using a simple
dynamic_cast
which will be a viable solution for the use case described. No one else mentioned this in the answers, and the way covariance is supported in C++ doesn't solve the OP's primary problem. -
Konrad Rudolph over 10 years@g-makulik Well, yeah, you could (should) have just removed or modified the contentious sentence – the rest is spot-on and a good answer. Now you’ve thrown out the child with the bath water.
-
πάντα ῥεῖ over 10 years@KonradRudolph Gave another one (leaving out the picky words). At least you're right: downvote rate was less than reputation gain at that point. But I'm getting nervous on downvotes, and experience shows they'll never be retracted on appropriately edited answers.
-
-
πάντα ῥεῖ over 10 yearsSo c++ covariant return types are pretty useless when we're talking about the deriving class's type itself, aren't they?
-
πάντα ῥεῖ over 10 years'if you would have called get_this() through a pointer to the dervied class then you would have gotten the right type with no compiler error' So c++ covariant return types are pretty useless when we're talking about the deriving class's type itself, aren't they?
-
πάντα ῥεῖ over 10 yearsI agree that this perfectly answers why the OP gets an error on compilation, but doesn't show up a viable alternative to get rid of the error.
-
Admin over 10 years@g-makulik They do allow you to get the right type when calling a method from the derived type. Otherwise you would have to cast.
-
πάντα ῥεῖ over 10 yearsThe crucial point for the OP's use case is, that you'll need to know the derived type to get the correct covariant return type of a function, thus it doesn't help much for downcasting (which is obviously what the OP wants here).
-
log0 over 10 years@g-makulik In fact I don't know any language with covariant return type where it does not work like this :/ when you are manipulating a
base
reference, it could be any type behind, so how could you know what should be the type ofx
... ? -
Admin over 10 years@g-makulik I never claimed the purpose of my answer was to propose an alternative. I am merely backing up Novelocrat's answer with appropriate excerpts from the standard, which I think is enlightening. I didn't propose downcasting with dynamic_cast primarily because I consider it to be a suspicious solution most of the time. Depending on his needs, maybe a better solution would be to simply add an appropriate virtual function that correctly executes the right code based on the dynamic object type. I mean, that's why virtual functions were invented in the first place.
-
Admin over 10 years@g-makulik Besides, the only thing the OP explicitly asked for was why he got the compilation error.
-
πάντα ῥεῖ over 10 yearsI also don't want to propagate down casting using
dynamic_cast
. That's why I've mentioned static polymorphism, which will be the better design choice in most cases IMHO. But it takes more efforts, to do it right. -
πάντα ῥεῖ over 10 years'Besides, the only thing the OP explicitly asked for ...' Yes, but you could clearly see his intend ;) ...
-
Admin over 10 years@g-makulik Perhaps, but I am declared explicit :)
-
RamblingMad almost 9 yearsAll of those methods are private and
type
is missing a type-specifier. You couldn't even instantiate an instance ofderived1
in your example. -
Alex over 7 yearsAlso the solution above can become your headache :)
-
John P over 6 yearsThis appears to be 'duck typing' based on 'magic numbers', or a manually/exhaustively-'enumerated type' minus uniqueness and the centralized index. I am a 'master' of nothing if not predicting headaches; I have to second @Alexander on that one.
-
John P over 6 yearsI would add that 'derivation' is not the same as 'divination' - you define derivatives of base classes, typically extending and refining each constructively. This is derivation, but derivation per the definition you would consider 'useful' is closer to the inverse. This is generally the price you pay for abstraction.