2010-10-08 23 views
16

¿Hay alguna razón por la cual se especifique que std::type_info es polimórfico? El destructor se especifica como virtual (y hay un comentario sobre el efecto de "para que sea polimórfico" en The Design and Evolution of C++). Realmente no puedo ver una razón convincente por qué. No tengo ningún caso de uso específico, solo me preguntaba si alguna vez hubo un razonamiento o historia detrás de esto.¿Por qué std :: type_info polymorphic?


Aquí hay algunas ideas que yo he llegado con y rechazado:

  1. Es un punto de extensibilidad - implementaciones podrían definir subclases, y programas podría luego tratar de dynamic_cast un std::type_info a otro, implementación- tipo derivado definido. Esta es posiblemente la razón, pero parece que es tan fácil para las implementaciones agregar un miembro definido por la implementación, que posiblemente sea virtual. Los programas que deseen probar estas extensiones necesariamente no serán portátiles de todos modos.
  2. Es para asegurar que los tipos derivados se destruyan correctamente cuando delete ing un puntero base. Pero no hay tipos derivados estándar, los usuarios no pueden definir tipos derivados útiles, porque type_info no tiene constructores públicos estándar, por lo que delete en un puntero type_info nunca es legal y portátil. Y los tipos derivados no son útiles porque no se pueden construir; el único uso que conozco para tales tipos derivados no construibles se encuentra en la implementación de elementos como el rasgo de tipo is_polymorphic.
  3. Deja abierta la posibilidad de metaclases con tipos personalizados: cada polimórfica real class A obtendría una "metaclase" derivada A__type_info, que deriva de type_info. Tal vez tales clases derivadas podrían exponer a los miembros que llaman al new A con varios argumentos de constructor de una manera segura para tipos, y cosas por el estilo. Pero hacer que type_info sea polimórfico hace que tal idea sea básicamente imposible de implementar, porque tendrías que tener metaclases para tus metaclases, ad infinitum, que es un problema si todos los objetos type_info tienen una duración de almacenamiento estática. Quizás excluir esto es la razón para hacerlo polimórfico.
  4. Se puede utilizar RTTI características (que no sean dynamic_cast) a std::type_info sí mismo, o alguien pensó que era lindo, o embarazoso si type_info no era polimórfico. Pero dado que no hay un tipo derivado estándar, y no hay otras clases en la jerarquía estándar a las que razonablemente se pueda probar con cross-cast, la pregunta es: ¿qué? ¿Hay algún uso para expresiones como typeid(std::type_info) == typeid(typeid(A))?
  5. Es porque los implementadores crearán su propio tipo derivado privado (como creo que lo hace GCC). Pero, ¿por qué molestarse en especificarlo? Incluso si el destructor no se especificó como virtual y un implementador decidió que debería serlo, seguramente esa implementación podría declararlo virtual, porque no cambia el conjunto de operaciones permitidas en type_info, por lo que un programa portátil no podría para ver la diferencia
  6. Tiene que ver con compiladores con incompatibilidades ABI parcialmente compatibles, posiblemente como resultado de la vinculación dinámica. Quizás los implementadores podrían reconocer su propia subclase type_info (a diferencia de una que se origina en otro proveedor) de forma portátil si se garantizara que type_info sería virtual.

El último es el más plausible para mí en este momento, pero es bastante débil.

Respuesta

9

Asumo que está ahí para la comodidad de los ejecutores. Les permite definir clases extendidas de type_info, y eliminarlas mediante punteros al type_info en la salida del programa, sin tener que crear una compilación mágica especial para llamar al destructor correcto, o saltar de otro modo a través de aros.

duda de que la aplicación podría declaran que virtual, porque no hace cambiar el conjunto de operaciones permitidas en type_info, por lo que un programa portátil no sería capaz de ver la diferencia .

No creo que sea cierto. Considere lo siguiente:

#include <typeinfo> 

struct A { 
    int x; 
}; 

struct B { 
    int x; 
}; 

int main() { 
    const A *a1 = dynamic_cast<const A*>(&typeid(int)); 
    B b; 
    const A *a2 = dynamic_cast<const A*>(&b); 
} 

Ya se trate de razonable o no, se permite que el primer grupo dinámico (y se evalúa como un puntero nulo), mientras que no se permite que el segundo modelo dinámico. Entonces, si type_info se definió en el estándar para tener el destructor no virtual predeterminado, pero una implementación agregó un destructor virtual, entonces un programa portátil podría diferenciar [*].

parece más sencillo para mí para poner el destructor virtual en la norma, que a cualquiera:

a) poner una nota en la norma de que, a pesar de la definición de clase implica que type_info no tiene funciones virtuales, se permite tener un destructor virtual.

b) determinar el conjunto de programas que pueden distinguir si type_info es polimórfico o no, y prohibirlos a todos. Puede que no sean programas muy útiles o productivos, no lo sé, pero para prohibirlos, debe encontrar un lenguaje estándar que describa la excepción específica que está haciendo a las reglas normales.

Por lo tanto, creo que el estándar tiene que ordenar el destructor virtual o prohibirlo. Hacerlo opcional es demasiado complejo (o quizás debería decir, creo que sería juzgado innecesariamente complejo. La complejidad nunca detuvo al comité de normas en áreas donde se consideró que valía la pena ...)

Si fue prohibido, sin embargo, a continuación, una aplicación podría:

  • añadir un destructor virtual en cierta clase derivada de type_info
  • derivar todos sus objetos TypeInfo de que clase
  • utilizar eso internamente como la clase de base polimórfica para todo

que resolvería la situación des anotado en la parte superior de la publicación, pero el tipo estático de una expresión typeid aún sería const std::type_info, por lo que sería difícil para las implementaciones definir extensiones donde los programas pueden dynamic_cast para ver qué tipo de objeto type_info tienen en un caso particular Tal vez el estándar esperaba permitir eso, aunque una implementación siempre podría ofrecer una variante de typeid con un tipo estático diferente, o garantizar que un static_cast a una cierta clase de extensión funcionará, y luego dejar que el programa dynamic_cast desde allí.

En resumen, por lo que yo sé que el destructor virtual es potencialmente útil a los ejecutores, y la eliminación no gana nadie que no sea que no sería pasar el tiempo preguntándose por qué está ahí ;-)

nada

[*] En realidad, no lo he demostrado. He demostrado que un programa ilegal podría, en igualdad de condiciones, compilarse. Pero una implementación quizás podría evitar eso al garantizar que no todo sea igual y que no compila. El is_polymorphic de Boost no es portátil, por lo tanto, aunque es posible que un programa pruebe que una clase es polimórfica, debería haber, es posible que no haya forma de que un programa conforme pruebe que una clase no es polimórfica, que no debería ser Sin embargo, creo que incluso si es imposible, probar que, para eliminar una línea del estándar, es un gran esfuerzo.

9

El estándar de C++ dice que typeid devuelve un objeto del tipo type_info, O una subclase definida por IMPLEMENTATION DEFINED del mismo. Entonces ... supongo que esta es más o menos la respuesta.Así que no veo por qué se rechaza sus puntos 1 y 2.

párrafo 5.2.8 Cláusula 1 de la corriente de C++ estándar lee:

El resultado de una expresión typeid es un lvalue de estática tipo const std :: type_info (18.5.1) y dinámica tipo const std :: type_info o const nombre donde nombre es una clase de implementación definidos derivado de std :: type_info que conserva el comportamiento descrito en 18.5. 1.61) Vida útil del objeto referido a por lvalue se extiende hasta el final de el programa. Si el destructor se llama o no para el objeto type_info al final del programa es no especificado.

que a su vez significa que se podría escribir el código siguiente es legal y fina: const type_info& x = typeid(expr); que puede requerir que sea polimórfica type_info

+1

Sí, entiendo eso, pero no puedo ver nada portátil que pueda hacer con la información de que es una subclase, o no. Parece que todos los programas portátiles y válidos ahora seguirían siendo válidos si no fueran polimórficos. Hacerlo para que no pueda detectar necesariamente las subclases (al hacer que type_info no sea polimórfico) no debería cambiar nada para los programas portátiles, hasta donde puedo ver, y no debería hacer las extensiones no portátiles más difíciles de crear. ¿Puedes pensar en un contraejemplo de algo hecho posible por esta especificación? – Doug

+0

@Doug: mira mi edición –

+1

Gracias, pero todavía no entiendo por qué esa última expresión requiere 'type_info' para ser polimórfica. Todas las funciones útiles en 'type_info', como' operator == ',' before', 'name', etc. no se especifican como virtuales. Entonces, si una implementación necesita despacho virtual en esas funciones, entonces supongo que puede declararlas virtuales, o delegarlas en funciones de implementación virtuales, y si no es así, entonces no es necesario que sean virtuales. ¿Se me escapa algo? – Doug

2

Acerca de la identificación "global" más simple que puede tener en C++ es un nombre de clase, y typeinfo proporciona una manera de comparar dichos identificadores para la igualdad. Pero el diseño es tan incómodo y limitado que debes ajustar typeinfo en alguna clase de contenedor, p. para poder poner instancias en colecciones. Andrei Alexandrescu lo hizo en su "Modern C++ Design" y creo que esa envoltura typeinfo es parte de la biblioteca Loki; probablemente haya uno también en Boost; y es bastante fácil hacer la tuya, p. ver my own wrapper.

Pero incluso para una envoltura de este tipo no hay en general ninguna necesidad de un destructor virtual en typeinfo.

La pregunta no es tanto "eh, ¿por qué hay un destructor virtual?" Sino más bien, como lo veo, "eh, ¿por qué el diseño es tan retrógrado, incómodo y no utilizable directamente?" Y lo atribuiría al proceso de estandarización. Por ejemplo, iostreams no son exactamente ejemplos de diseño excelente, tampoco; no es algo para emular

+1

Tengo una especie de respuesta a "eh, ¿por qué el diseño es tan retrógrado, incómodo y no directamente utilizable?". Bjarne Stroustrup (a) quería disuadir a la gente de confiar demasiado en RTTI y (b) recibió muchas solicitudes de todo tipo de funciones relacionadas con RTTI, algunas de las cuales violaron la seguridad de C++. Pensó que sería un montón de problemas para satisfacer las solicitudes de todos, además de ser potencialmente alto y técnicamente dudoso, por lo que era más fácil proporcionar una característica básica que las personas pudieran construir de acuerdo con sus necesidades. – Doug

2

3/deja abierta la posibilidad de metaclases con tipos personalizados - cada verdadero polimórfica class A se pueden conseguir una derivada "metaclase" A__type_info, que deriva de type_info. Tal vez tales clases derivadas podrían exponer a los miembros que llaman al new A con varios argumentos de constructor de una manera segura para tipos, y cosas por el estilo.Pero hacer que type_info sea polimórfico hace que tal idea sea básicamente imposible de implementar, porque tendrías que tener metaclases para tus metaclases, ad infinitum, que es un problema si todos los objetos type_info tienen una duración de almacenamiento estática. Quizás excluir esto es la razón para hacerlo polimórfico.

Clever ...

De todos modos, no estoy de acuerdo con este razonamiento: dicha aplicación podría fácilmente descartar una meta clases de tipos derivados de type_info, incluyendo type_info sí.

Cuestiones relacionadas