By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,963 Members | 920 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,963 IT Pros & Developers. It's quick & easy.

virtual inheritance downcast

P: n/a
#include <iostream>
using namespace std;

class Base
{
public:
Base() {}
~Base() {}
};

class Derived : virtual public Base
{
public:
Derived() {}
~Derived() {}
};

int main()
{
Base *b = new Derived();
Derived *d = (Derived*)b; //line 22. The problem line!

return 0;
}

For the code above, I got the following error message:
downcast.cc: In function `int main()':
downcast.cc:22: error: cannot convert from base `Base' to derived type `
Derived' via virtual base `Base'

That means it is illegal to explicit convert a VIRTUAL base point to the
derived point. (non-virtual is ok) Can somebody explain it? ( The coding
style is bad, please just ignore it. ^_^)

Thanks in advance!
Apr 19 '06 #1
Share this Question
Share on Google+
8 Replies


P: n/a
fl****@gmail.com wrote:
#include <iostream>
using namespace std;

class Base
{
public:
Base() {}
~Base() {}
};

class Derived : virtual public Base
{
public:
Derived() {}
~Derived() {}
};

int main()
{
Base *b = new Derived();
Derived *d = (Derived*)b; //line 22. The problem line!

return 0;
}

For the code above, I got the following error message:
downcast.cc: In function `int main()':
downcast.cc:22: error: cannot convert from base `Base' to derived type `
Derived' via virtual base `Base'

That means it is illegal to explicit convert a VIRTUAL base point to the
derived point. (non-virtual is ok) Can somebody explain it? ( The coding
style is bad, please just ignore it. ^_^)


The compiler is correct - the conversion is illegal. Equivalently (and
more specifically), you cannot downcast a virtual base class pointer to
a derived class pointer using static_cast. This is because the case may
involve some pointer adjustment that depends on the dynamic type of the
object pointed to. e.g.

class Base
{
char i;
};

class Derived : virtual public Base
{
int j
};

class Derived2 : virtual public Base
{
int k;
};

class DiamondBottom: public Derived, public Derived2
{
int l;
};

int main()
{
Base *b = new DiamondBottom();
Derived *d = static_b; //line 22. The problem line!

return 0;
}

Now, in a DiamondBottom object, the Derived2 subobject is likely to be
placed after the Derived subobject, which means that the pointer
adjustment required for a conversion from a Base* to a Derived2* depends
on whether the Derived2 is part of a larger DiamondBottom object or not.

E.g. a Derived2 object looks like this (vtable pointers left out):
i
k

while a DiamondBotton object might look like this (vtable pointers left
out):
i
j
k
l

so that i and k are in different relative positions depending on the
dynamic type of the object.

The solution is to use dynamic_cast, which performs the correct
conversion by checking the real type of the object.

Tom
Apr 19 '06 #2

P: n/a
* fl****@gmail.com:
#include <iostream>
using namespace std;

class Base
{
public:
Base() {}
~Base() {}
};

class Derived : virtual public Base
{
public:
Derived() {}
~Derived() {}
};

int main()
{
Base *b = new Derived();
Derived *d = (Derived*)b; //line 22. The problem line!
Yes, don't use C-style casts. A C-style cast tells the compiler that
your Really Know What You're Doing. Which you absolutely don't, here.

return 0;
}

For the code above, I got the following error message:
downcast.cc: In function `int main()':
downcast.cc:22: error: cannot convert from base `Base' to derived type `
Derived' via virtual base `Base'

That means it is illegal to explicit convert a VIRTUAL base point to the
derived point. (non-virtual is ok) Can somebody explain it? ( The coding
style is bad, please just ignore it. ^_^)


Due to the virtual inheritance the Base sub-object can be a shared Base
sub-object for any number of Derived objects. So which one should the
cast produce a pointer to? There's no way to tell, strictly locally,
from the static type information, and for consistency the rules don't
distinguis situations like above where the compiler /could/ have figured
it out by looking at the rest of the program text.

So to do this you need access to run time type information, what the
object really is when the cast is attempted, and you need to allow for
failure.

And to do that, use a dynamic_cast (which requires at least one virtual
member function in Base, e.g. the destructor).

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Apr 19 '06 #3

P: n/a

<fl****@gmail.com> wrote in message news:e2**********@news.cn99.com...
#include <iostream>
using namespace std;

class Base
{
public:
Base() {}
~Base() {} virtual ~Base() {} };

class Derived : virtual public Base
{
public:
Derived() {}
~Derived() {}
};

int main()
{
Base *b = new Derived();
Derived *d = (Derived*)b; //line 22. The problem line! Derived *d = dynamic_cast<Derived *>(b);

if you don't have virtual members in base class, then you can't use
dynamic_cast. In that case, reinterpret_cast is the only option but it will
cause undefined behavior on some platform...
return 0;
}

For the code above, I got the following error message:
downcast.cc: In function `int main()':
downcast.cc:22: error: cannot convert from base `Base' to derived type `
Derived' via virtual base `Base'

That means it is illegal to explicit convert a VIRTUAL base point to the
derived point. (non-virtual is ok) Can somebody explain it? ( The coding
style is bad, please just ignore it. ^_^)

Thanks in advance!

Apr 21 '06 #4

P: n/a
Fei Liu wrote:
dynamic_cast. In that case, reinterpret_cast is the only option but it will
cause undefined behavior on some platform...


The reinterpret_cast creates a value of a new type that has the SAME
BIT pattern as its argument so it will cause undefined behavior in ANY
platform, not some platforms from the base class to the derived class.

FYI, here is a test case:
======================
#include <iostream>
using namespace std;

class Base
{
public:
float base_v;
Base(): base_v(999) { }
~Base() {}

};

class Derived : virtual public Base
{
public:
int derived_v;
Derived(): derived_v(0) {}
~Derived() {}

};

int main()
{
Base *b = new Derived();
Derived *d = reinterpret_cast<Derived *>(b); //line 22. The
problem line!

cout << "b->base_v=" << b->base_v << endl;

//d->derived_v shows a weired result
cout << "d->derived_v=" << d->derived_v << endl;

// the following will throw segamentation fault!
cout << "d->base_v=" << d->base_v << endl;

return 0;
}
==============================

Apr 21 '06 #5

P: n/a
dan2online wrote:
dynamic_cast. In that case, reinterpret_cast is the only option but it
will
cause undefined behavior on some platform...
The reinterpret_cast creates a value of a new type that has the SAME
BIT pattern as its argument so it will cause undefined behavior in ANY
platform, not some platforms from the base class to the derived class.


....
class Derived : virtual public Base


Nice sample, but if you take the 'virtual' out, then on some platforms the
address of the Derived will be the same as the address of the Base within
it. You can assert for that.

In that situation, this rule takes effect: "reinterpret_cast works correctly
if it casts a pointer or reference to a type that an address truly
contains".

(Someone will doubtless look the correct verbiage up for me: My copy of the
old Standard is on my old hard drives.)

So in that narrow situation the behavior is defined.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Apr 21 '06 #6

P: n/a

Phlip wrote:
dan2online wrote:

Nice sample, but if you take the 'virtual' out, then on some platforms the
address of the Derived will be the same as the address of the Base within
it. You can assert for that.


If the "virtual" disappears, the original example has no problem then.

Apr 21 '06 #7

P: n/a
Phlip wrote:
dan2online wrote:
dynamic_cast. In that case, reinterpret_cast is the only option but it
will
cause undefined behavior on some platform...


The reinterpret_cast creates a value of a new type that has the SAME
BIT pattern as its argument so it will cause undefined behavior in ANY
platform, not some platforms from the base class to the derived class.

class Derived : virtual public Base


Nice sample, but if you take the 'virtual' out, then on some platforms the
address of the Derived will be the same as the address of the Base within
it. You can assert for that.


If you leave the virtual in, it might still be the case. Of course, you
can't
compare a Derived* and a Base*; so for your assert you'd still need to
cast the other side of the comparison as well (or the first side back,
depending on how you write the assert). So the assert may cause
undefined behavior; it's a bit tricky to say without code.

Still, IIRC it's not the creation of the Derived* that will trip you,
but the actual
use as if it indeed points to a Derived*. You should be able to cast it
back to
the original Base*. Not very useful, I admit.

This doesn't matter a lot when it comes to Undefined Behavior and
platforms.
Undefined Behavior is what the ISO standard says about a piece of code.

If it's UB according to the standard, it's UB for all compilers, and if
not, for none.
(Disregarding trivial code like const int i = 4/sizeof(int)-4. A
division by zero is
still UB everywhere)

HTH,
Michiel Salters

Apr 21 '06 #8

P: n/a
Fei Liu wrote:

if you don't have virtual members in base class, then you can't use
dynamic_cast. In that case, reinterpret_cast is the only option but it will
cause undefined behavior on some platform...

Actually the C-style cast here will do a static_cast. This isn't
undefined as long as you know that the dynamic type really is
the type being cast to.
Apr 27 '06 #9

This discussion thread is closed

Replies have been disabled for this discussion.