Are dynamic_casts safe to remove in production code?
dynamic_cast
s are slow开发者_开发问答er, but they are safer than static_cast
s (when used with object hierarchies, of course). My question is, after I've ensured in my debug code that all (dynamic) casts are correct, is there any reason for me not to change them to static_cast
s?
I plan on doing this with the following construct. (Btw, can you think of a better name than assert_cast
? Maybe debug_cast
?)
#if defined(NDEBUG)
template<typename T, typename U>
T assert_cast(U other) {
return static_cast<T>(other);
}
#else
template<typename T, typename U>
T assert_cast(U other) {
return dynamic_cast<T>(other);
}
#endif
Edit: Boost already has something for this: polymorphic_downcast
. Thanks to PlasmaHH for pointing that out.
No! dynamic_cast
does more than just casting. It can check the runtime type of the object. But it can also traverse hierarchies that are unknown to the compiler, but are only known in runtime. static_cast
cannot do that.
For example:
class A1
{
virtual ~A1() {}
};
class A2
{
virtual ~A2() {}
};
class B : public A1, public A2
{ };
A1 *a1 = new B;
A2 *a2 = dynamic_cast<A2*>(a1); //no static_cast!
A1 *x = ...;
if (B *b = dynamic_cast<B*>(x)) //no static_cast!
/*...*/;
You should assert that the dynamic_cast
succeeded:
template<typename T, typename U>
T *assert_cast(U *other) {
T *t = dynamic_cast<T>(other);
assert(t);
return t;
}
Replacing dynamic_cast
with static_cast
in situation when you are sure that they are equivalent is the same as removing null checks for pointer that you are sure is always non-null. You can do that for performance reasons.
As long as you've tested with every possible combination of runtime factors/variables/inputs, sure. As mentioned in the comments, this would be akin to removing assertions.
There's nothing in the language that would make this inherently unsafe, given the requisite assurances that your casts will always be correct. It does feel inherently unsafe, though, in that you could probably never make such a guarantee.
Update
Konstantin has proved that when dealing with multiple inheritance this technique will only work for single steps up/down the inheritance tree1.
struct A1 { virtual ~A1() {} };
struct A2 { virtual ~A2() {} };
struct A3 { virtual ~A3() {} };
struct B : A1, A2 {};
struct C : A1, A3, A2 {};
int main() {
A1* a1 = (rand() < RAND_MAX / 2 ? (A1*)new B : (A1*)new C);
A2* p1 = dynamic_cast<A2*>(a1);
// ^ succeeds, but is a cross-cast
// A2* p2 = static_cast<A2*>(a1);
// ^ ill-formed
A2* p3 = static_cast<A2*>(static_cast<B*>(a1));
// ^ must chain, instead.
// but p3 is invalid because we never
// checked that `dynamic_cast<B*>(a1)` is valid.. and it's not
// Instead, let's expand the `dynamic_cast`s into a chain, too:
A2* p3 = dynamic_cast<B*>(a1);
A2* p4 = dynamic_cast<A2>*(a1);
// ^ p3 fails, so we know that we cannot use `static_cast` here
}
So, you can replace your dynamic_cast
s with static_cast
s iff:
- Each
dynamic_cast
performs only a single step up or down; - Each
dynamic_cast
is known to always succeed.
1 Actually this is a bit of a simplification as, for example, downcasts will work for any number of steps. But it makes a good rule of thumb.
after I've ensured in my debug code that all (dynamic) casts are correct, is there any reason for me not to change them to static_casts?
IMHO, If you are 100% sure that all dynamic_cast<>
are correct, then there is no reason for not changing them to static_cast<>
. You can change them.
I guess it depends on project. If it is nuclear power station management software then I would prefer safety, if it is 3D game I would prefer performance. You can never be sure that all dynamic_cast
will be correct in production code. If performance is more important than safety then remove.
精彩评论