Re: Why does the C++ spec. prohibit downcasting through non-public inheritance?
edam <edam@waxworlds.org> writes:
But now consider
this code:
struct B {
virtual ~B() {};
};
struct D : protected B {
void foo() {
B &b = dynamic_cast< B & >( *this ); // upcast ok here
D &d = dynamic_cast< D & >( b ); // runtime error
}
friend class F;
};
struct F {
void foo( D &dref ) {
B &b = dynamic_cast< B & >( dref ); // upcast ok here
D &d = dynamic_cast< D & >( b ); // runtime error
}
};
void main() {
D d;
d.foo();
F f;
f.foo( d );
}
Here, we successfully upcast from a derived class reference to a
protected base class reference in two places: from a member function
of
the derived class and from a member function of a friend class.
We also attempt to downcast from a protected base class reference to a
derived class reference in the same two places--an action that would
succeed if we weren't using protected inheritance. Both attempts fail
with std::bad_cast thrown.
I've been having a look at this again, having *thought* that I'd
deciphered from the standard what I should expect to happen in relation
to your code here. That is /should/ happen as your results (for
gcc-4.4.3) appear to indicate probably surprised me as much as it did
you.
What surprises me even more, having recognised that there have been a
number of bugs in gcc over how access control is handled (though none
specifically in relation to use of static_cast, as I could find), I went
back to try a version of your code in the most recent release, half
expecting - from my reading of [expr.dynamic.cast] ?5.2.7/8 - that it
would fail even the upcasts, only to find something very different.
If we take the code above and add the headers <iostream> and <typeinfo>,
correct the return on main and add a couple of debug print statements
there, we get:
21:23:34 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $cat dyn_cast_ex1.cpp
// file: dyn_cast_ex1.cpp
#include <iostream>
#include <typeinfo>
struct B {
virtual ~B() {};
};
struct D : protected B {
void foo() {
B &b = dynamic_cast< B & >( *this ); // upcast ok here
D &d = dynamic_cast< D & >( b ); // runtime error
}
friend class F;
};
struct F {
void foo( D &dref ) {
B &b = dynamic_cast< B & >( dref ); // upcast ok here
D &d = dynamic_cast< D & >( b ); // runtime error
}
};
int main() {
D d;
try {
d.foo();
} catch (std::bad_cast) {
std::cout << "std::bad_cast from d.foo()\n";
}
F f;
try {
f.foo( d );
} catch (std::bad_cast) {
std::cout << "std::bad_cast from f.foo(D)\n";
}
}
21:23:42 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $i686-pc-cygwin-g++-4.4.3 -o
dyn_cast_ex1 dyn_cast_ex1.cpp |
|
21:24:27 Paul Bibbings@JIJOU |
/cygdrive/d/CPPProjects/CLCPP $./dyn_cast_ex1 |
std::bad_cast from d.foo() // fail <---+
std::bad_cast from f.foo(D) // fail <---+
21:24:33 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $i686-pc-cygwin-g++-4.5.0 -o
dyn_cast_ex1 dyn_cast_ex1.cpp |
|
21:25:01 Paul Bibbings@JIJOU |
/cygdrive/d/CPPProjects/CLCPP $./dyn_cast_ex1 |
// OK? <---+
21:25:07 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $
So. gcc-4.5.0 doesn't regard there to be a problem here at all, and I'm
.... confused now.
Regards
Paul Bibbings