En resumen, debido a la herencia múltiple.
En larga:
#include <iostream>
struct A { int a; };
struct B { int b; };
struct C : A, B { int c; };
int main() {
C c;
std::cout << "C is at : " << (void*)(&c) << "\n";
std::cout << "B is at : " << (void*)static_cast<B*>(&c) << "\n";
std::cout << "A is at : " << (void*)static_cast<A*>(&c) << "\n";
}
Salida:
C is at : 0x22ccd0
B is at : 0x22ccd4
A is at : 0x22ccd0
Tenga en cuenta que el fin de convertir correctamente a B *, static_cast tiene que cambiar el valor del puntero. Si el compilador no tuviera la definición de clase para C, entonces no sabría que B era una clase base, y ciertamente no sabría qué compensación aplicar.
Pero en esa situación en la que no hay una definición es visible, static_cast no se comporta como reinterpret_cast, está prohibido:
struct D;
struct E;
int main() {
E *p1 = 0;
D *p2 = static_cast<D*>(p1); // doesn't compile
D *p3 = reinterpret_cast<D*>(p1); // compiles, but isn't very useful
}
Un molde sencillo de estilo C, (B*)(&c)
hace lo que dice: si la definición de la estructura C es visible, mostrando que B es una clase base, luego es lo mismo que un static_cast. Si los tipos solo son forward-declarated, entonces es lo mismo que reinterpret_cast. Esto se debe a que está diseñado para ser compatible con C, lo que significa que tiene que hacer lo que hace C en los casos que son posibles en C.
static_cast siempre sabe qué hacer para los tipos incorporados, eso es realmente lo que tiene incorporado medio. Puede convertir int a float, y así sucesivamente. Por eso siempre es seguro para los tipos numéricos, pero no puede convertir los punteros a menos que (a) sepa a qué apuntan, y (b) existe el tipo correcto de relación entre los tipos apuntados. Por lo tanto, puede convertir int
en float
, pero no int*
en float*
.
Como dice AndreyT, hay una manera que puede utilizar static_cast
manera insegura, y el compilador probablemente no te salvará, porque el código es legal:
A a;
C *cp = static_cast<C*>(&a); // compiles, undefined behaviour
Una de las cosas que puede hacer es static_cast
"downcast" un puntero a una clase derivada (en este caso, C es una clase derivada de A). Pero si la referencia no es realmente de la clase derivada, estás condenado. Un dynamic_cast
realizaría una comprobación en tiempo de ejecución, pero para mi clase de ejemplo C no puede usar un dynamic_cast
, porque A no tiene funciones virtuales.
También puede hacer cosas inseguras con static_cast
ay desde void*
.
La herencia múltiple no es el único problema, la respuesta de AndreyT aborda el problema de la transmisión descendente al tipo incorrecto. –
Cierto, solo contesté completamente el primer párrafo de la pregunta, y parcialmente el segundo párrafo. No es el problema general (en el título) de la seguridad static_cast. –