2

I stumbled upon casting sideways from derived to derived class and discovered a gap in my knowledge. I had been living in a world where this is possible - until now. Instead, std::bad_cast is thrown. What's going on here?

#include <iostream>

class Base
{
protected:
    int x_;

public:
    Base(int x) : x_(x) {}
    virtual ~Base() = default;
    int x() const { return x_; }
    virtual void setX(int x) = 0;
};

class Editable : public Base // class implements setters
{
public:
    Editable(int x) : Base(x) {}
    void setX(int x) { x_ = x; }
};

class ReadOnly : public Base // class implements empty setters
{
public:
    ReadOnly(int x) : Base(x) {}
    void setX(int x) {}
};

int main()
{
    Editable editable(4);
    ReadOnly &readOnly = dynamic_cast<ReadOnly&>(editable); // std::bad_cast
}
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • 3
    Sure, it can cast sideways (though it's not really in the sense you are using), but the thing you are casting must still actually be a `ReadOnly` (or a class derived from it)... – T.C. Apr 12 '15 at 09:25
  • "Unlike other casts, a dynamic_cast involves a run-time type check. If the object bound to the pointer is not an object of the target type, it fails and the value is 0. If it's a reference type when it fails, then an exception of type bad_cast is thrown." – SChepurin Apr 12 '15 at 09:35

2 Answers2

5

This is what is meant when people say that dynamic_cast can cast sideways:

struct A { virtual ~A() = default; };
struct B { virtual ~B() = default; };
struct D : A, B {};

B* pb = new D();
A* pa = dynamic_cast<A*>(pb); // OK

i.e., it allows you to cast a B* to a A* if the pointer is pointing to something that's derived from both A and B. For the cast to succeed, there still must be an A subobject for the pointer to point to (or the reference to bind to, if you are casting to a reference type).

In your case, e is an Editable. There's no ReadOnly subobject anywhere in there. So the cast fails.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Thank you, so the problem was my misinterpretation of sideways casting. I will make `Editable` inherit from `ReadOnly` to gain the same functionality and eliminate `Base`. – LogicStuff Apr 12 '15 at 09:38
  • @LogicStuff: If you go with that solution, you might change the name from `ReadOnly` to `Readable`, since an instance of `Editable` is clearly not something that is read only! –  Apr 12 '15 at 10:16
2

You're not casting sideways.
You're casting from one derived of a base to another.

Let's hypothetically say your cast succeeded, and there's a public int member called readOnlyInt in ReadOnly class.

How can the compiler perform readonly.readOnlyInt?
There's no readOnlyInt in the casted ReadOnly & cause it is in fact an Editable.

Casting sideways involves multiple inheritance structures & looks something like this:

class A {
    virtual ~A() {}
};

class B {
    virtual ~B() {}
};

class C : public A, public B {
};

int main() {
    B *b = new C();
    A *a = dymanic_cast<A *>(b);

    return 0;
}
ArnonZ
  • 3,822
  • 4
  • 32
  • 42