Topic: down casting with virtual inheritance - what is the rationale?


Author: "Conrad Weyns" <weyns@online.no>
Date: Sun, 18 Mar 2001 18:32:59 GMT
Raw View
Question for the language lawyers about down casting.
(Please, no lecture about the evils of down casting).

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

class B : public virtual A
{
 public:
    virtual ~B() {}
};

void down_cast_test(A* pA)
{
    // 1. Error: illegal cast
    B* pB = static_cast<B*>(pA);

    // 2. Ok but not interesting! I understand why this is legal.
    B* pB = dynamic_cast<B*>(pA);
}

If B's inheritance to A was not virtual then the static_cast (1.) would be
legal.
I'd like to understand the rationale behind this.
I don't see what information the compiler is missing to figure that one out.

Thanks in advance!
Conrad Weyns.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Jonathan Biggar <jon@floorboard.com>
Date: Sun, 18 Mar 2001 22:50:52 GMT
Raw View
Conrad Weyns wrote:
>
> Question for the language lawyers about down casting.
> (Please, no lecture about the evils of down casting).
>
> class A
> {
>  public:
>     virtual ~A() {};
> };
>
> class B : public virtual A
> {
>  public:
>     virtual ~B() {}
> };
>
> void down_cast_test(A* pA)
> {
>     // 1. Error: illegal cast
>     B* pB = static_cast<B*>(pA);
>
>     // 2. Ok but not interesting! I understand why this is legal.
>     B* pB = dynamic_cast<B*>(pA);
> }
>
> If B's inheritance to A was not virtual then the static_cast (1.) would be
> legal.
> I'd like to understand the rationale behind this.
> I don't see what information the compiler is missing to figure that one out.

Add the following classes to your example:

class C : public B
{
    ...
};

class D : public B
{
    ...
};

class E : public C, public D
{
    ...
};

Now try this:

    A *pA = new E;
    B *pB = static_cast<B*>(pA);

If static_cast worked in your example above, which B should it return
here?

Or even simpler:

class F : public virtual A
{
    ...
};

    A *pA = new F;
    B *pB = static_cast<B*>(pA);

The static_cast isn't supposed to examine the run time context, so how
can it distinguish between your example (presumably correct) and this
one (which is clearly erroneous.)

--
Jon Biggar
Floorboard Software
jon@floorboard.com
jon@biggar.org

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Olaf Krzikalla <Entwicklung@reico.de>
Date: Mon, 19 Mar 2001 17:39:04 GMT
Raw View
Hi,

Jonathan Biggar wrote:
> Or even simpler:
>
> class F : public virtual A
> {
>     ...
> };
>
>     A *pA = new F;
>     B *pB = static_cast<B*>(pA);
>
> The static_cast isn't supposed to examine the run time context, so how
> can it distinguish between your example (presumably correct) and this
> one (which is clearly erroneous.)

IMHO this example misses the point. The static_cast (if allowed) would
return a wrong pointer at runtime regardless of the virtual inheritance.
About 2 or 3 weeks ago I gave an (german) explanation of the different
casts in de.comp.lang.iso-c++. There was also described, why static_cast
and virtual inheritance would cause trouble. If you can't find the
article or don't speak german, I could try to translate the important
part.


Olaf

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Mon, 19 Mar 2001 18:29:12 GMT
Raw View
Conrad Weyns wrote:
>
> Question for the language lawyers about down casting.
> (Please, no lecture about the evils of down casting).
>
> class A
> {
>  public:
>     virtual ~A() {};
> };
>
> class B : public virtual A
> {
>  public:
>     virtual ~B() {}
> };
>
> void down_cast_test(A* pA)
> {
>     // 1. Error: illegal cast
>     B* pB = static_cast<B*>(pA);
>
>     // 2. Ok but not interesting! I understand why this is legal.
>     B* pB = dynamic_cast<B*>(pA);
> }
>
> If B's inheritance to A was not virtual then the static_cast (1.) would be
> legal.
> I'd like to understand the rationale behind this.
> I don't see what information the compiler is missing to figure that one out.

If it were a lack of information that were relevant, then the
dynamic_cast<> wouldn't be possible either. The issue is not the lack of
information; it's just a division of labor. The specialized casts were
created to give programmers more control over what kind of cast will
occur. You choose the kind of cast that performs the conversion you want
to perform. You get a warning when you try to use it to do a type of
cast it can't perform. Warnings are our friends!

Basically, static_cast<> performs casts that depend only upon the static
types of the objects. dynamic_cast<> performs casts that depend upon the
dynamic type of the object. With non-virtual inheritence, static_cast<>
simply performs something that is roughly equivalent
(B*)((char*)pA+offsetof(A,B)), except that it works even on non-POD
structs.

However, when going from a virtual base to a derived class, the offset
from A to B depends upon the dynamic type. The most-derived type might
not even have a B sub-object, in which case the cast must fail. There
might be more than one, in which case the cast must also fail. If it
contains exactly one B sub-object, the offset of that object from the
corresponding A sub-object depends upon what other classes virtually
derived from A are in the inheritance tree, and where those objects are
in the tree. That's why this is considered a dynamic cast.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: marco@technoboredom.net (Marco Manfredini)
Date: Mon, 19 Mar 2001 21:18:01 GMT
Raw View
"Conrad Weyns" <weyns@online.no> wrote in
<aD2t6.1892$mh4.84145@news3.oke.nextra.no>:

>Question for the language lawyers about down casting.
>(Please, no lecture about the evils of down casting).
>
>class A
>{
> public:
>    virtual ~A() {};
>};
>
>class B : public virtual A
>{
> public:
>    virtual ~B() {}
>};
>
>void down_cast_test(A* pA)
>{
>    // 1. Error: illegal cast
>    B* pB = static_cast<B*>(pA);
>
>    // 2. Ok but not interesting! I understand why this is legal.
>    B* pB = dynamic_cast<B*>(pA);
>}
>
>If B's inheritance to A was not virtual then the static_cast (1.)
>would be legal.
>I'd like to understand the rationale behind this.
>I don't see what information the compiler is missing to figure that
>one out.
>

The point with virtual base classes is, that the compiler has no idea
where the virtual base class is located within the complete object. In
fact, most implementations would carry an offset in B which the compiler
would add to "this" to determine the location of the A part at runtime.

class A {/*stuff*/}

class B : public virtual A {/*stuff*/}
class C : public virtual A {/*stuff*/}
class D : public B,C {/*stuff*/}

Since C und B have A virtual, in the resulting D you get one A. B and C
must have some kind of pointer to access their shared A.

It was like you had:

class A {/*stuff*/}

class B : { A* aPartOfB; /*stuff*/}
class C : { A* aPartOfC; /*stuff*/}
class D : public B,C { A a; aPartOfB=&a; aPartOfC=&a; }

Now you see that the compiler can't tell from a A* alone where the B*
was (if the A* would point to a B), since the B part could be anywhere..

--
ola ola
coca cola.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "online" <weyns@online.no>
Date: Tue, 20 Mar 2001 14:20:00 GMT
Raw View
"Olaf Krzikalla" <Entwicklung@reico.de> wrote in message
news:3AB64319.C946DEC8@reico.de...
| Hi,
|
| Jonathan Biggar wrote:
| > Or even simpler:
| >
| > class F : public virtual A
| > {
| >     ...
| > };
| >
| >     A *pA = new F;
| >     B *pB = static_cast<B*>(pA);
| >
| > The static_cast isn't supposed to examine the run time context, so how
| > can it distinguish between your example (presumably correct) and this
| > one (which is clearly erroneous.)
|
| IMHO this example misses the point. The static_cast (if allowed) would
| return a wrong pointer at runtime regardless of the virtual inheritance.

I agree entirely! It could barely be clearer.

| About 2 or 3 weeks ago I gave an (german) explanation of the different
| casts in de.comp.lang.iso-c++. There was also described, why static_cast
| and virtual inheritance would cause trouble. If you can't find the
| article or don't speak german, I could try to translate the important
| part.

Olaf,
This post has, particularly on the comp.lang.c++ sister group, received a
number of responses that trouble me - to say the least.
I accept a rationale based on a c++ language decision: "Downcasting from a
virtual base class shall only be done with a dynamic_cast". End of story!

Many have responded by contriving typical examples using multiple
inheritance that would clearly result in ambiguous casts, whether static or
dynamic. I was not interested in that.
I am trying hard to understand, why the compiler, in my example, pretends
not to know the layout of a B instance.

My german is poor: I could not find your article on the German newsgroup. I
would not want you to use time translating it, but perhaps you could simply
send me a copy at weyns@online.no and I'll get some help with it here.

Thanks for your time.
Conrad Weyns.



|
|
| Olaf
|
| ---
| [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
| [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
| [              --- Please see the FAQ before posting. ---               ]
| [ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
|

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Geert Denys <gdenys@yahoo.com>
Date: Tue, 20 Mar 2001 21:12:46 GMT
Raw View
Conrad Weyns wrote:
>
> Question for the language lawyers about down casting.
> (Please, no lecture about the evils of down casting).
>

The explanation lies in the implementation of virtual inheritance.
Although in the example shown, one could easily devise a correct
solution, this solution would fail in the context of further derived
classes of B.

In general, a class containing one or more virtual base classes is
divided into an invariant region and a shared region. The shared
region represents the virtual base class subobjects. The important
point is that the location of the data within the shared region
fluctuates with each derivation.

Hence,
 B* pB = static_cast<B*>(pA);
instructs the compiler to assume that pA references an object of class
B *or* a subclass of B. Since the exact location of the subobject A
within its B object is not known (because we may deal with a subclass
of B) in the context of virtual inheritance, the compiler cannot
statically determine B.

For more background, I think "Inside the C++ object model" by Lippman
is pretty good.

Hope this helps!

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "online" <weyns@online.no>
Date: Wed, 21 Mar 2001 19:11:42 GMT
Raw View
Geert and all others that have contributed to this thread: Many thanks!

"Geert Denys" <gdenys@yahoo.com> wrote in message
news:3AB78FDD.5942FA76@yahoo.com...
| Conrad Weyns wrote:

[]

| Hence,
| B* pB = static_cast<B*>(pA);
| instructs the compiler to assume that pA references an object of class
| B *or* a subclass of B.

You hit the nail right on the head so to speak. Many others including those
contributing on the comp.lang.c++ group have done so as well, I now realize,
albeit in a very round about way.

The key word here is "assume". Exactly! That was allways my point of origin
and that is presicely what threw me! Suddenly, for no apparent reason, the
compiler assumed a position of higher authority, that of trying to prevent
me from shooting myself in the foot! Yet I knew for a fact that it could
easily have helped me. I think the overall conclusion I should draw is
simply this:

Due to the fact that the programmer probably does not know what object
he/she is actually dealing with, the c++ language simply instructs compilers
to implement the golden rule: "Thau shalt not allow a down cast from a
virtual base with a static_cast". End of story!

I accept that, eventhough I am surprised to say the least! C++ is not
exactly known for not giving the programmer the benifit of the doubt! I have
yet to see a sizeable project where down casting, old type casting and very
doggy reinterpret casting to and from void* is not a regular occurance,
often in quite complex multiple inheritance hierarchies that for instance
require a particular base class to be "left most" or else - all hell breaks
loose! With that in mind, my example should be like childs play for any
compiler!

[]

| For more background, I think "Inside the C++ object model" by Lippman
| is pretty good.

It's on it's way!
Many thanks,
Conrad Weyns.


|
| Hope this helps!
|
| ---
| [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
| [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
| [              --- Please see the FAQ before posting. ---               ]
| [ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
|

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]