Reputation: 133577
I have a structure like the following:
struct managed_object {
virtual ~managed_object() { }
};
class trait1 {
public:
virtual void myMethod() const = 0;
};
class trait2 {
public:
virtual void myOtherMethod(int x) const = 0;
};
class MyType final : public managed_object, public trait1 {
...
};
class MyType2 final : public managed_object, public trait1, public trait2 {
...
};
class wrapper {
private:
managed_object* ptr;
public:
template<typename T> T* object() const {
return dynamic_cast<T*>(data.ptr);
}
};
So basically I have a managed_object
base class from which multiple types inherit. Each of this subtypes can inherit from any combination of traits and they are final
so that I'm sure they won't have any deeper level on inheritance.
The code works thanks to RTTI which takes the load of gluing everything together but at a price, otherwise
wrapper w = ...
trait* asTrait1 = w.object<trait1>;
wouldn't work because there's no direct relation between managed_object
and trait1
types.
In my complete code I'm already sure that all dynamic_cast
won't fail because I have additional data (not shown in the example) which provides me a sort of RTTI that is needed for other parts of the code.
Given that, is there a common pattern to solve the side-downcast problem without using the need of dynamic_cast
and RTTI assuming that I can already know that a MyType
class is inheriting from a specific trait
? I'm trying to find a clever solution since it's a heavy bottleneck of the code.
Upvotes: 4
Views: 5952
Reputation: 5729
First things first: You have to use static_cast
. reinterpret_cast
isn't really suitable for this.
But in order for the cast to work, your program needs to know where it is going. By that I mean, it needs to know the path it must take to cast from A
to B
. If A
and B
are on the same class hierarchy, this is simple: just follow said class hierarchy to perform the cast.
But if you have:
struct C: A, B {};
And this is the only relation between A
and B
, static_cast
has no way of knowing about C
(this is the kind of information provided by RTTI), and hence it cannot perform the cast, since A
and B
are not really related.
In order to provide that path, you have to enable your program to know it somehow. The easiest way is templating wrapper
:
template<typename T>
class wrapper {
managed_object* ptr;
public:
template<typename Trait> Trait* object() const {
return static_cast<Trait*>(static_cast<T*>(ptr));
}
};
Then:
MyType a;
wrapper<MyType> w{&a};
trait1* asTrait1 = w.object<trait1>(); // OK
Note that I'm telling exactly how to do the cast, by first downcasting to the derived type, then "upcasting" back to the trait.
reinterpret_cast
If you are converting from a class to its Base (MyType
to trait1
), dynamic_cast
will return a pointer or reference to the base class subobject within the derived object (static_cast
can also make this conversion properly). This means that the value of the returned pointer may actually be different than the value of the provided pointer. reinterpret_cast
will never make such a change to the pointer value. It will merely reinterpret whatever is passed to it as the new type, which is clearly wrong. The obvious conclusion is to not use reinterpret_cast
to perform casts within class hierarchies.
Upvotes: 1
Reputation: 31465
You cannot use dynamic_cast
without RTTI. Except for a few corner cases.
You can use static_cast
or reinterpret_cast
(please don't) though, but then it's on you if you get it wrong - then you can no longer test for nullptr
to see if the cast succeeded
Upvotes: 5