Re: dynamic_cast does not work as specified
On Mon, 12 Jan 2009 00:18:20 -0500, Joseph M. Newcomer
<newcomer@flounder.com> wrote:
Here's a problem I hit that is inconsistent with the documentation.
I'm handling an OnUpdate notification, where the CObject* can be one of several types
depending on the value of lHint.
So the code is basically
void CMyView::OnUpdate(CView * pSender, LPARAM lHint, CObject *pHint)
{
if(lHint == 0 && pHint == NULL)
{ /* normal update */
CView::OnUpdate(pSender, lHint, pHint);
return;
} /* normal update */
if(lHint == WHATEVER)
{ /* update the whatever */
SubThing * p = dynamic_cast<SubThing *>pHint;
ASSERT(p != NULL);
if(p != NULL)
DoSomething(p);
} /* update the whatever */
} // CMyView::OnUpdate
where I have defined
class Thing { ... };
class SubThing : public Thing { ... };
That's not gonna work, because Thing is not derived from CObject. More
generally, CObject appears nowhere in the inheritance graph. You know
beforehand that the dynamic_cast will fail, always.
If I try to single-step into it, it tries to trace a source file along some bizarre path
for rtti.cpp, but this source is not included in the distribution. Bummer.
So, I thought, it might have to do with the fact that (due to exceptionally poor design
decisions in the original MFC design) the pHint is declared to be a CObject*, unrelated to
a Thing or SubThing (in a rational world, it would have been an LPVOID). The line
SubThing * p = dynamic_cast<SubThing *>pHint;
generates the compile-time diagnostic:
C2681: 'CObject *': invalid expression type for dynamic_cast
I have no idea why this is an invalid expression for dynamic_cast. But it may deal with
some obscure, undocumented rule.
The documentation is actually a little better than the error message:
http://msdn.microsoft.com/en-us/library/87ea2et5(VS.80).aspx
<q>A casting operator tried to convert from an invalid type.</q>
It's still a little misleading, because the type CObject* is a valid source
type provided CObject is in the inheritance graph for the destination type.
It's an "invalid type" /here/ because it doesn't appear in the inheritance
graph for your type.
But dynamic_cast is defined to work on, among other expressions, a pointer-to-void, so I
tried
LPVOID ptr = (LPVOID)pHint;
SubThing * p = dynamic_cast<SubThing *>p;
but that produces an error
C2681: 'LPVOID': invalid expression type for dynamic_cast
in spite of the documetnation of dynamic_cast, which *clearly* states:
The type-id must be a pointer or a reference to a previously defined class type or
a "pointer to void".
If MSDN is referring to the type you're casting to, it's correct. You can
dynamic_cast /to/ void*, in which case, you get a pointer to the "complete
object". You can't cast /from/ void*, though, because there's no way to
know if has any type info attached to it. (Which would be through a pointer
in the vtbl for typical implementations - but dynamic_cast can't know the
object pointed to has a vtbl.)
It turns out I can get around this by doing
Thing * ptr = (Thing *)pHint;
SubThing * p = dynamic_cast<SubThing *>ptr;
So does this represent an error in the documentation, or an error in the implementation?
Maybe the interpretation. :)
--
Doug Harrison
Visual C++ MVP