开发者

Are dynamic_casts safe to remove in production code?

开发者 https://www.devze.com 2023-04-03 14:00 出处:网络
dynamic_casts are slow开发者_开发问答er, but they are safer than static_casts (when used with object hierarchies, of course). My question is, after I\'ve ensured in my debug code that all (dynamic) ca

dynamic_casts are slow开发者_开发问答er, but they are safer than static_casts (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_casts?

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_casts with static_casts 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号