2011-02-10 2 views
14

У меня смешанные чувства по поводу static_cast, так как это самый безопасный листинг C++, но он позволяет одновременно безопасные и небезопасные конверсии, поэтому вы должны знать контекст, чтобы сказать, действительно ли он безопасен или может привести к UB (например, при сдаче подкласса).Не используется static_cast?

Так почему же нет более безопасного явного литья? Вот пример, где это может быть полезно. В COM, они должны вернуть указатель на интерфейс, как void** ppv, так что «должны» бросить явно

*ppv = (IInterface*) this; 

, который затем было предложено заменить на более безопасный C++ отбрасываемой

*ppv = static_cast<IInterface*>(this); 

Но это сделать смысл сделать даже static_cast здесь? this имеет класс, производный от IInterface, поэтому можно было бы просто написать

IInterface* p = this; // implicit conversion to base, safe for sure 
*ppv = p; 

или использовать помощника, как

template<class T, class U> 
T implicit_cast(U p) { return p; } 

*ppv = implicit_cast<IInterface*>(this); 

Итак, это правда, что static_cast иногда неправильно и может (должен?) быть замененным на это implicit_cast в некоторых случаях, или я что-то упускаю?

EDIT: Я знаю, что a cast is required in COM, но это не обязательно должно быть static_cast, неявного броска будет достаточно.

+1

Примечание: См http://stackoverflow.com/a/869597 для "правильной" реализации 'implicit_cast' (и хороший объяснение). –

ответ

5

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

Похоже, что использование вашего implicit_cast будет более безопасным и позволит вам явно выбрать, какой базовый класс вы хотите косвенно использовать (что, по-видимому, требуется для COM).

Я сделал быстрый тест с g ++ и implicit_cast действительно возвращает разные адреса для разных базовых классов, как и ожидалось.

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

Тестовая программа:

#include <iostream> 

class B1 
{ 
public: 
    virtual ~B1() {} 
}; 

class B2 
{ 
public: 
    virtual ~B2() {} 
}; 

class Foo : public B1, public B2 
{ 
}; 

template<class T, class U> 
T implicit_cast(U p) { return p; } 

int main() 
{ 
    Foo* f = new Foo; 
    void **ppv = new void*; 

    *ppv = implicit_cast<B1*>(f); 
    std::cout << *ppv << std::endl;; 
    *ppv = implicit_cast<B2*>(f); 
    std::cout << *ppv << std::endl;; 

    return 0; 
} 
+0

Действительно, 'dynamic_cast' был бы более безопасным, чем' static_cast', но я не упоминал об этом, потому что я никогда не видел его на самом деле (я слышал о хите производительности, но не знаю, насколько он большой). –

+2

@ 7vies: В Visual C++ это около 2 тысяч циклов процессора, и компилятор не поймает вас, если вы нанесете свой COM-объект на неправильный тип - вы получите только нулевой указатель во время выполнения. – sharptooth

+0

@sharptooth: Да, 'dynamic_cast' определенно не самый лучший вариант в контексте COM. Я даже не уверен, в какой ситуации это может быть хорошим вариантом - в большинстве случаев его можно заменить полиморфизмом времени выполнения или компиляции, который будет более безопасным и быстрым в одно и то же время. –