c++ - remix - smart contracts ethereum
C++ no puede convertir desde la base A al derivado tipo B a través de la base virtual A (7)
$ 5.2.9 / 2- "Una expresión e se puede convertir explícitamente a un tipo T usando un static_cast de la forma static_cast (e) si la declaración" T t (e); "está bien formada, para alguna variable temporal inventada t (8.5) ".
En tu código estás intentando static_cast con ''T = B *'' y ''e = A *''
Ahora ''B * t (A *)'' no está bien formado en C ++ (pero ''A * t (B *)'' se debe a que ''A'' es una base virtual inequívoca y accesible de ''B''. Por lo tanto, el código da error .
Tengo tres clases:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D: public B, public C {};
Intentando un lanzamiento estático de A * a B * obtengo el siguiente error:
cannot convert from base A to derived type B via virtual base A
No puede usar static_cast
en esta situación porque el compilador no conoce el desplazamiento de B relativo a A en el tiempo de compilación. El desplazamiento debe calcularse en tiempo de ejecución en función del tipo exacto del objeto más derivado. Por lo tanto, debe usar dynamic_cast
.
No sé si esto es "seguro", pero
Asumiendo
B derivado de A (y A pura virtual)
Dado que SÉ que un puntero a B sigue siendo un puntero a B.
class A
{
virtual void doSomething(const void* p) const =0;
};
class B
{
public:
int value;
virtual void doSomething(const void*p)const
{
const B * other = reinterpret_cast<const B*>(p);
cout<<"hello!"<< other->value <<endl;
}
};
int main()
{
B foo(1),bar(2);
A * p = &foo, q=&bar;
p->doSomething(q);
return 0;
}
este programa se ejecuta y devuelve correctamente la impresión "¡Hola!" y el valor del otro objeto (en este caso "2").
por cierto, lo que estoy haciendo es muy inseguro (personalmente doy una identificación diferente a cada clase y afirmo después de reinterpretar que la ID actual es igual a otra ID para estar seguro de que estamos haciendo algo con 2 clases iguales) y como ves, me limité a métodos "const". Por lo tanto, esto funcionará con métodos "no const", pero si haces algo mal, la captura del error será casi imposible. E incluso con la afirmación, hay 1 oportunidad de 4 billones para tener éxito en la afirmación, incluso cuando se supone que falla (afirmar (ID == other-> ID);)
Por cierto ... Un buen diseño de OO no debería requerir este tipo de cosas, pero en mi caso traté de refactorizar / rediseñar el código sin poder dejar de utilizar el reinterpreteo. en general, puedes evitar este tipo de cosas.
Para entender el sistema de transmisión, debes sumergirte en el modelo de objetos.
La representación clásica de un modelo de jerarquía simple es la contención: que si B
deriva de A
entonces el objeto B
de hecho contendrá un subobjeto A
junto con sus propios atributos.
Con este modelo, downcasting es una manipulación de puntero simple, mediante un desplazamiento conocido en el tiempo de compilación que depende del diseño de la memoria de B
Esto es lo que hace static_cast : un molde estático se dobla estático porque el cálculo de lo que es necesario para el elenco se realiza en tiempo de compilación, ya sea aritmética de puntero o conversiones (*).
Sin embargo, cuando virtual
herencia virtual
activa, las cosas tienden a ser un poco más difíciles. El problema principal es que con virtual
herencia virtual
todas las subclases comparten una misma instancia del subobjeto. Para hacer eso, B
tendrá un puntero a A
, en lugar de A
correctamente, y el objeto de la clase base A
será instanciado fuera de B
Por lo tanto, es imposible en el tiempo de compilación deducir la aritmética de puntero necesaria: depende del tipo de tiempo de ejecución del objeto.
Siempre que haya una dependencia de tipo de tiempo de ejecución, necesita RTTI (información de tipo de tiempo de ejecución), y el uso de RTTI para conversiones es el trabajo de dynamic_cast .
En resumen:
-
static_cast
tiempo de compilación:static_cast
- tiempo de ejecución de
dynamic_cast
:dynamic_cast
Los otros dos también son lanzamientos en tiempo de compilación, pero son tan específicos que es fácil recordar para qué son ... y huelen mal, así que mejor no usarlos de todos modos.
(*) Según lo observado por @curiousguy en los comentarios, esto solo se aplica para downcasting. Un static_cast
permite upcasting independientemente de la herencia virtual o simple, aunque el lanzamiento también es innecesario.
Por lo que yo sé, necesitas usar dynamic_cast
porque la herencia es virtual
y estás bajando.
Sí, debe usar un dynamic_cast, pero tendrá que hacer que la clase base A sea polimórfica, por ejemplo, agregando un dtor virtual.
Según documentos estándar,
Sección 5.2.9 - 9 , para Static Cast ,
Un valor de tipo "puntero a cv1 B", donde B es un tipo de clase, se puede convertir a un valor r de tipo "puntero a cv2 D", donde D es una clase derivada (cláusula 10) de B, si es un estándar válido la conversión de "puntero a D" a "puntero a B" existe (4.10), cv2 es la misma calificación cv, o mayor calificación cv que, cv1, y B no es una clase base virtual de D ni una clase base de una clase base virtual de D.
Por lo tanto, no es posible y debes usar dynamic_cast
...