Безопасны ли динамические_касты в производственном коде? - PullRequest
9 голосов
/ 09 сентября 2011

dynamic_cast с медленнее, но они безопаснее, чем static_cast с (конечно, при использовании с иерархиями объектов). Мой вопрос заключается в том, что после того, как я убедился в своем отладочном коде, что все (динамические) приведения верны, есть ли причина для меня не менять их на static_cast s?

Я планирую сделать это с помощью следующей конструкции. (Кстати, вы можете придумать более подходящее имя, чем assert_cast? Может быть 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

Редактировать: В Boost уже есть что-то для этого: polymorphic_downcast. Спасибо PlasmaHH за указание на это.

Ответы [ 5 ]

4 голосов
/ 09 сентября 2011

Нет!dynamic_cast делает больше, чем просто кастинг.Он может проверить тип времени выполнения объекта.Но он также может проходить иерархии, которые неизвестны компилятору, но известны только во время выполнения.static_cast не может этого сделать.

Например:

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!
  /*...*/; 
2 голосов
/ 09 сентября 2011

Пока вы проверяли все возможные комбинации факторов времени / переменных / входных данных, конечно. Как упоминалось в комментариях, это было бы похоже на удаление утверждений.

В языке нет ничего, что сделало бы это по сути небезопасным, учитывая необходимые гарантии того, что ваши высказывания всегда будут правильными. Тем не менее, это кажется небезопасным в том смысле, что вы, вероятно, никогда не сделаете такую ​​гарантию.


Обновление

Константин доказал, что при работе с множественным наследованием этот метод будет работать только для одиночных шагов вверх / вниз по дереву наследования 1 .

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
}

Таким образом, вы можете заменить dynamic_cast s на static_cast s, если:

  • Каждый dynamic_cast выполняет только одиночный шаг вверх или вниз;
  • Каждый dynamic_cast, как известно, всегда добивается успеха.

1 На самом деле это немного упрощает, так как, например, откаты будут работать для любого количества шагов. Но это хорошее правило.

2 голосов
/ 09 сентября 2011

Вы должны утверждать, что dynamic_cast успешно:

template<typename T, typename U>
T *assert_cast(U *other) {
    T *t = dynamic_cast<T>(other);
    assert(t);
    return t;
}

Замена dynamic_cast на static_cast в ситуации, когда вы уверены, что они эквивалентны, равнозначно удалению нулевых проверок для указателя, которыеВы уверены, что всегда ненулевой.Вы можете сделать это по соображениям производительности.

1 голос
/ 09 сентября 2011

после того, как я убедился в своем отладочном коде, что все (динамические) приведения правильны, есть ли причина для меня не менять их на static_casts?

ИМХО, если вына 100% уверены, что все dynamic_cast<> верны, тогда нет никаких оснований для того, чтобы не изменить их на static_cast<>.Вы можете изменить их.

1 голос
/ 09 сентября 2011

Я думаю, это зависит от проекта. Если это программное обеспечение для управления атомной электростанцией, то я бы предпочел безопасность, если это 3D-игра, я бы предпочел производительность. Вы никогда не можете быть уверены, что все dynamic_cast будут правильными в рабочем коде. Если производительность важнее безопасности, удалите.

...