C++ no puede convertir de la base A al tipo derivado B a través de la base virtual A


Tengo tres clases:

class A {};

class B : virtual public A {};
class C : virtual public A {};

class D: public B, public C {};

Al intentar 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
Author: curiousguy, 2010-09-19

7 answers

Para comprender el sistema de fundición, debe sumergirse en el modelo de objetos.

La representación clásica de un modelo de jerarquía simple es la contención: que si B se deriva de A entonces el objeto B de hecho contendrá un subobjeto A junto con sus propios atributos.

Con este modelo, downcasting es una simple manipulación de puntero, por un desplazamiento conocido en el tiempo de compilación que depende del diseño de memoria de B.

Esto es lo que static_cast do: un cast estático se denomina static porque el cálculo de lo que es necesario para el cast se realiza en tiempo de compilación, ya sea aritmética de puntero o conversiones (*).

Sin embargo, cuando virtual la herencia entra en acción, las cosas tienden a volverse un poco más difíciles. El problema principal es que con virtual herencia todas las subclases comparten una misma instancia del subobjeto. Para hacer eso, B tendrá un puntero a A, en lugar de a A propiamente, y la clase base A el objeto se instanciará fuera de B.

Por lo tanto, es imposible en tiempo de compilación poder 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 hacer uso de RTTI para casts es el trabajo de dynamic_cast.

En resumen:

  • en tiempo de compilación: static_cast
  • bajada en tiempo de ejecución: dynamic_cast

Los otros dos también son casts en tiempo de compilación, pero son tan específicos que es fácil recordar para qué son... y son malolientes, así que mejor no usarlos en absoluto de todos modos.

(*) Como señaló @curiousguy en los comentarios, esto solo se aplica a la reducción de emisiones. A static_cast permite el upcasting independientemente de la herencia virtual o simple, aunque entonces el cast también es innecesario.

 83
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-26 13:58:27

Por lo que sé, necesitas usar dynamic_cast porque la herencia es virtual y estás perdiendo.

 11
Author: Jon Purdy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-19 19:06:38

No se puede usar static_cast en esta situación porque el compilador no conoce el desplazamiento de B relativo a A en 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.

 6
Author: ybungalobill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-19 19:20:07

Sí, tiene que usar un dynamic_cast, pero tendrá que hacer que la clase base sea polimórfica, por ejemplo, agregando un dtor virtual.

 4
Author: Nico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-19 19:28:34

De acuerdo con los documentos estándar,

Sección 5.2.9 - 9, for Static Cast ,

Un rvalue de tipo "puntero a cv1 B", donde B es un tipo de clase, se puede convertir en un rvalue de tipo "puntero a cv2 D, "donde D es una clase derivada (cláusula 10) de B, si una conversión estándar válida de " puntero a D " a " puntero a B" existe (4.10), cv2 es la misma calificación cv que, o mayor calificación cv que, cv1, y B no es ni una clase base virtual de D ni una clase base de una clase base virtual de D.

Por lo tanto, no es posible y debe usar dynamic_cast...

 4
Author: liaK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-20 06:01:29

$5.2.9/2- "Una expresión e puede ser convertido 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 formado, para algunos 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*)' es porque 'A' es una base virtual inequívoca y accesible de 'B'. Por lo tanto, el código da error.

 1
Author: Chubsdad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-20 13:07:15

No se si esto es "seguro" pero.

Suponiendo

B derivado de A (y Un virtual puro)

Ya 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 ejecuta y devuelve correctamente la impresión "hello!"y el valor del otro objeto (en este caso "2").

Por cierto, lo que estoy haciendo es altamente inseguro (personalmente doy un ID diferente a cada clase y afirmo después de reinterpretar el casting que el ID actual es igual a otro ID para estar seguros de que estamos haciendo algo con 2 clases iguales) y como ves me limité a los métodos "const". Por lo tanto, esto funcionará con métodos "no constantes", pero si haces algo mal, atrapar el error será casi imposible. E incluso con la afirmación hay una posibilidad de 1 de 4 mil millones para tener éxito afirmación incluso cuando se supone que debe fallar (assert (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 ser capaz de abandonar el uso de reinterpretar casting. en términos generales, PUEDE evitar este tipo de cosas.

 1
Author: GameDeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-17 06:43:03