Re: Why does the C++ spec. prohibit downcasting through non-public
inheritance?
On 30.04.2010 17:15, * Paul Bibbings:
"Alf P. Steinbach"<alfps@start.no> writes:
* edam:
[...] I cannot understand why a member function of the derived
class or of a friend class can not downcast a protected base class
reference to a derived class reference specifically because we are
using protected inheritance.
Consider a class hierarchy Base and SomeoneElsesDerived with protected
inheritance from Base.
If you could downcast from Base to your own protected class then you
could upcast from SomeoneElsesDerived and down to your own class,
gaining access to Base stuff in that SomeoneElsesDerived object.
'protected' would then not mean anything
In order for this to be understood as an answer to the OP's question, it
would need to be the case that described upcast-plus-downcast would be
okay where protected inheritance was not used.
You're right that what I wrote doesn't make sense and is incorrect. I was
thinking of static_cast, because the access issue is not about dynamic_cast as
such. And then I goofed in the explanation. Good intentions. Bad communication.
OK, if at first you don't succeed, try again.
...
Example of how the dynamic failure of dynamic_cast corresponds to compilation
failure for static_cast:
<code>
class Base
{
public:
virtual ~Base() {}
};
class Derived: protected Base
{
public:
void foo( Base& o )
{
static_cast<Derived&>( o );
}
};
void bar( Base& o )
{
static_cast<Derived&>( o ); // Nope.
}
int main() {}
</code>
For static_cast this protects you against using knowledge of the derivation from
Base to treat Derived objects as Base objects or vice versa. The protected
derivation says that it's an implementation detail, and e.g. in the future might
be replaced by a Base* data member, or a different class, whatever.
If the static_cast labeled "Nope" above is replaced with dynamic_cast, then it
compiles.
But it then compiles almost no matter which class is specified instead of
Derived&, e.g. a completely unrelated class FooBar& -- and this is perhaps
what baffled the OP?
To make it not compile with dynamic_cast, one must specify a class where it's
known at compile time that the dynamic_cast must fail, e.g. an inaccessible
sub-object's class. Otherwise it compiles fine. But even though it compiles, it
fails at run time unless there is a possible sequence of static_casts (honoring
accessibility) that produces the desired reference or pointer:
<code>
#include <stdio.h>
class Base
{
public:
virtual ~Base() {}
};
class Derived1: protected Base {};
class Derived2: public Base {};
void bar( Base& o )
{
printf( "%p\n", (void*)dynamic_cast<Derived1*>( &o ) );
printf( "%p\n", (void*)dynamic_cast<Derived2*>( &o ) );
printf( "\n" );
}
int main()
{
Derived1 o1;
Derived2 o2;
bar( (Base&)o1 );
bar( (Base&)o2 );
}
</code>
<output>
00000000
00000000
00000000
0022FF40
</output>
Are you suggesting that
the following would `succeed'?:
struct Base { virtual ~Base() { } };
struct SomeoneElsesDerived : public Base { };
struct MyDerived : public Base { };
int main()
{
SomeoneElsesDerived sed;
Base& b = dynamic_cast<Base&>(sed);
MyDerived& md = dynamic_cast<MyDerived&>(b);
}
As it clearly would not, can you expand on how an example that fails in
all instances can be used as an argument against what the OP expects
should happen in a specific one?
It's an issue with static_cast. And sorry for first of all failing to mention
that, and secondly for goofing up the explanation further, talking about how one
can't cast down successfully (true for dynamic_cast but not for static_cast).
But as an example of what I had in mind, static_cast-wise:
<code>
#include <stdio.h>
#include <stack>
#include <deque>
using namespace std;
void show( stack<int> const& s )
{
struct Hack: stack<int>
{
static deque<int> const& containerOf( stack<int> const& s )
{
Hack const& h = static_cast<Hack const&>( s );
deque<int> const& d = h.c; // A protected member of std::stack,
?23.2.3.1/1
return d;
}
};
deque<int> const& q = Hack::containerOf( s );
for( int i = 0; i < int( q.size() ); ++i )
{
printf( "%i ", q[i] );
printf( "\n" );
}
}
int main()
{
stack<int> s;
s.push( 1 );
s.push( 2 );
s.push( 3 );
show( s );
}
</code>
Now if std::stack was polymorphic and the static_cast was replaced with
dynamic_cast, this hack would fail. Which is nice since it's formally UB.
static_cast says "please don't check anything except accessibility and type
compabtibility", dynamic_cast says "please check not only accessibility and type
compatibility but also the actual dynamic type".
Again, sorry for the first article, but I hope this clarifies things.
Cheers,
- Alf