Complex dynamic_cast in c++

958 views Asked by At

I have the following case in C++:

  • Abstract base classes Abstract1 and Abstract2. They are unrelated.
  • A class Foo deriving from both Abstract1 and Abstract2

I am in a compilation unit where I have no information about class Foo (no declaration, no definition). Only Abstract1 and Abstract2 are known. (Actually, Foo is even defined in a DLL)

Will dynamic_cast allow casting from Abstract1* to Abstract2*? Is this a standard?

4

There are 4 answers

0
Columbo On BEST ANSWER

What you describe is a so-called cross-cast. For dynamic_cast<T>(v), the standard specifies in [expr.dynamic.cast]/8

If C is the class type to which T points or refers, the run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a C object [..]

  • Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to the C subobject of the most derived object.

That will work even with no information about Foo's existence in the translation unit that contains the cast.

You should check out this question too.

0
Ad N On

Yes it will work.

dynamic_cast is based on RTTI. The information provided by RTTI here is enough to determine the actual dynamic type of the pointed to object. By definition, RTTI is a run time notion, as is the dynamic type of the pointed to object (the fact that Foo's definition is not available in a compilation unit where said cast is written is a compile time notion, with no relevance here).

  • If the pointed to object is actually a Foo, the dynamic_cast will succeed at run time.
  • If it is not a pointer to an object deriving from Abstract2, it will fail (returning a null pointer).

details

A possible implementation of dynamic_cast is to look up a special member at the beginning of the object's memory layout (or it could be stored along the v-table). This structure could contain a value, which identifies the dynamic type of the object. Somewhere, the compiler would have generated a static table, replicating all the information about your program inheritance diagram. At run time, the cast would extract the type identifier of your instance, and check it against the static table. If this identifier refers to a type deriving from Abstract2, the cast is meaningful (and the code can return a pointer correctly offset to the Abstract2 interface of your object).

Even this naïve implementation never requires the knowledge of Foo in the compilation unit where the cast is written.

3
Lightness Races in Orbit On

Well, you could simply have tried it!

#include <cassert>

struct IBase1
{
    virtual void foo() = 0;
    virtual ~IBase1() {}
};

struct IBase2
{
    virtual void bar() = 0;
    virtual ~IBase2() {}
};

struct Derived : IBase1, IBase2
{
    void foo() {}
    void bar() {}
};

int main()
{
    Derived d;

    IBase1* ptr = &d;
    assert(dynamic_cast<IBase2*>(ptr));
    assert(dynamic_cast<Derived*>(ptr));
}

// Compiles successfully

And here's the proof:

[C++11: 5.2.7/8]: If C is the class type to which T points or refers, the run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a C object, and if only one object of type C is derived from the subobject pointed (referred) to by v the result points (refers) to that C object.
  • Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to the C subobject of the most derived object.

  • Otherwise, the run-time check fails.

Colloquially, we call this cross-casting.

There is no requirement stipulated by the language that the most derived object's type be known about in the "current" translation unit; it's up to the implementation to make that work and, in the common "virtual tables" model, indeed it does.

0
barak manos On

For this code:

void func(Abstract1* a1)
{
    Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
    ...
}

You're asking:

If a1 is pointing to a Foo object, will the dynamic-cast return a valid object pointer?

The answer is yes:

  • During runtime, the dynamic-cast will identify the V-Table of a1 as the V-Table of class Foo.
  • Since class Foo inherits from class Abstract2, the dynamic-cast will return a valid pointer.