0

after reading casts in C++ I understand that dynamic_cast should retrieve the derived class when it is "lost". The following example proves me wrong. but I quite don't get it:

the attempt to dynamic_cast (B* po = dynamic_cast(&ao);) makes compilation to fail, although an old fashioned cast will make the compilation succeed, and the run will go as expected.

//g++  5.4.0
#include <iostream>
using namespace std;

class A{
    protected:
    int x;
    A(int _x) : x(_x) {}
    public:
    A() : x(-1) {}
    void p() { cout << "(A) x = " << x << endl; };
    ~A(){ cout << "destroy A" << endl; }
};

class B : public A{
    public:
    B() : A(-2) {}
    void p() { cout << "(B) x = " << x << endl; };
};

A& f( A& o ) { return o; }

int main()
{
    cout << "start\n";
    {
        B o;
        A ao = f(o);
        ao.p();
        //B* po = dynamic_cast<B*>(&ao); //this fails with following error :
        // source_file.cpp:32:37: error: cannot dynamic_cast ‘& ao’ (of type ‘class A*’) to type ‘class B*’ (source type is not polymorphic)
        B* po = (B*)(&ao);
        po->p();
    }
    cout << "stop\n";
}

//trace
//
//start
//start
//(A) x = -2
//(B) x = -2
//destroy A
//destroy A
//stop
chetzacoalt
  • 145
  • 10
  • 6
    The error message is self-descriptive: the base class `A` should be polymorphic one, i.e. have at least one virtual function. – vahancho Feb 11 '20 at 15:50
  • 1
    From the page you linked to but didn't read in its entirety: "You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited)." – molbdnilo Feb 11 '20 at 15:52
  • Try changing `ao` to `A&&`? – Roy Avidan Feb 11 '20 at 15:53
  • 3
    Once you get this to compile, it will still not work since `ao` is not a `B` instance. Read about "object slicing". – molbdnilo Feb 11 '20 at 15:55
  • 2
    To elaborate on molbdnilo's comment: You have to change `A ao = f(o);` to `A& ao = f(o);` (note the ampersand indicating that `ao`is not an object but a *reference* to an object) or possibly `A *ao = &f(o);` Here the ampersand, in a different location, is the "address-of" operator, and `ao` now is a *pointer* to the base of `o`, which is a `B`. (It would help to name variables properly -- a good name for a `B` is `b`...) The reason is that polymorphism only works with pointers or references to base classes which actually refer to objects of a derived class. You cannot change *object* types. – Peter - Reinstate Monica Feb 11 '20 at 15:59
  • @vahancho : I still don't get it. I added "virtual void f() {}" in class A. no other modification, same trace. And if I uncomment "B* po = dynamic_cast(&ao);" and comment "B* po = (B*)(&ao);" I get (at runtime) """Invalid memory reference (SIGSEGV)""".. What are your suggesting I change ? – chetzacoalt Feb 11 '20 at 16:06
  • @peter : alright, this works! thanks – chetzacoalt Feb 11 '20 at 16:07
  • to follow up @Peter-ReinstateMonica's comment, [you were slicing the object](https://stackoverflow.com/questions/274626/what-is-object-slicing) – NathanOliver Feb 11 '20 at 16:08

1 Answers1

0

For dynamic_cast<> to work, the object needs to have a vtable. Otherwise, there is no indication within the object to which dynamic class it belongs.

A vtable is added to a C++ class if, and only if that class contains at least one virtual function.

So, add at least one virtual function to the class, and the dynamic cast should work.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • 1
    While this is in practice correct I didn't upvote because I didn't like the vtable slang. Who cares about that. The type must be polymorphic by which means ever. – Peter - Reinstate Monica Feb 11 '20 at 16:19
  • P.S. I like to see so many Monica tags pop up. I mentioned "reinstate Monica" each time SE Inc. asked me "what can we do better?" or "What is missing in your opinion?" in the [developer survey.](https://stackoverflow.blog/2020/02/05/the-2020-developer-survey-is-now-open/) Y'all, take it and make our voices heard. – Peter - Reinstate Monica Feb 11 '20 at 16:20
  • @Peter-ReinstateMonica Your view is that of the high-level user who programs according to the standard. My view is that of someone who knows what's under the hood, and thus knows many of the reasons why things in the standard are the way they are. I've found knowing this "why" always more rewarding than just knowing the rules, especially because typically several rules are derived from the same fundamental "why". So, knowing the low-level "why" is a form of optimization, imho. – cmaster - reinstate monica Feb 11 '20 at 17:01
  • In this concrete case, if you know that there is something like a vtable that's referenced from each class's instance via an invisible pointer, you know what a virtual method call does, how `dynamic_cast<>` can do its work, what a member function pointer is, and so on, and so forth. Yes, it takes some additional learning, and no, learning this stuff is certainly not for everyone. But I like to mention it anyways in the hope of providing a deeper insight to some of my readers. – cmaster - reinstate monica Feb 11 '20 at 17:06