¿Por qué es std :: type_info polimórfica?
-
28-09-2019 - |
Pregunta
¿Hay una razón por la std::type_info
se especifica a ser polimórficos? El destructor se especifica a ser virtual (y hay un comentario en el sentido de "por lo que es polimórfico" en el diseño y evolución de C ++). Realmente no puedo ver una razón de peso por qué. No tengo ningún caso de uso específico, me estaba preguntando si alguna vez hubo una razón o historia detrás de él.
Aquí hay algunas ideas que yo he llegado con y rechazado:
- Es un punto de extensibilidad - implementaciones podrían definir subclases, y programas podrían luego tratar de
dynamic_cast
unstd::type_info
a otro, tipo derivado definido por la implementación. Esta es posiblemente la razón, pero parece que es igual de fácil para implementaciones para añadir un miembro de aplicación definidos, lo que podría ser virtual. Los programas que desean prueba para estas extensiones serían necesariamente todos modos no portátil. - Es para asegurarse de que los tipos derivados se destruyen correctamente cuando
delete
ing un puntero base. Pero no hay tipos estándar derivada, los usuarios no pueden definir tipos de derivados útiles, porquetype_info
tiene constructores públicos estándar, y asídelete
ing un punterotype_info
no es legal y portátil. Y los tipos derivados no son útiles porque no pueden ser construidos - el único uso que sé para tales tipos derivados no construibles es en la implementación de cosas como el rasgo de tipois_polymorphic
.
- deja abierta la posibilidad de metaclases con tipos personalizados - cada
class A
polimórfica real sería conseguir unA__type_info
derivada "metaclase", que se deriva detype_info
. Quizás tales clases derivadas pueden exponer a los miembros quenew A
llamada con diversos argumentos del constructor en una forma de tipo seguro, y cosas por el estilo. Pero haciéndose polimórficatype_info
realidad hace que tal idea básicamente imposible de implementar, ya que tendría que tener metaclases para sus metaclases, hasta el infinito, que es un problema si todos los objetostype_info
tienen una duración de almacenamiento estático. Tal vez Salvo esto es la razón de lo que es polimórfica. - Hay alguna utilidad para aplicar características de RTTI (excepto
dynamic_cast
) astd::type_info
sí mismo, o pensó que alguien que era lindo, o embarazoso sitype_info
no era polimórfica. Pero dado que no hay ningún tipo derivado estándar, y no hay otras clases en la jerarquía estándar que se podría intentar razonablemente cruzada fundido a, la cuestión es: ¿qué? ¿Hay un uso para expresiones tales comotypeid(std::type_info) == typeid(typeid(A))
? - Se debe a que los ejecutores crearán su propio tipo derivado privada (como creo que hace GCC). Pero, ¿por qué molestarse especificándolo? Incluso si el destructor no se especificó como virtual y un ejecutor decidió que debería ser, sin duda que la aplicación pueda declarar virtual, porque no cambia el conjunto de operaciones permitidas en
type_info
, por lo que un programa portátil no sería capaz a notar la diferencia. - Es algo que ver con los compiladores parcialmente compatibles con coexistente ABIs, posiblemente como resultado de la vinculación dinámica. Tal vez los ejecutores podían reconocer su propia subclase
type_info
(en contraposición a una proveniente de otro proveedor) de una manera portátil, sitype_info
se garantiza que sea virtual.
La última es la más plausible para mí en este momento, pero es bastante débil.
Solución
Asumo que está ahí para la comodidad de los ejecutores. Se les permite definir clases type_info
extendidos, y eliminarlos a través de punteros a type_info
al salir del programa, sin tener que construir en la magia especial compilador para llamar al destructor correcta, o no pasar por el aro.
duda de que la aplicación podría os lo virtual, porque no lo hace cambiar el conjunto de operaciones permitidas en type_info, por lo que un programa portátil no sería capaz de decir la diferencia.
No creo que eso 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 evalúa a un puntero nulo), mientras que no se permite que el segundo modelo dinámico. Por lo tanto, si type_info
se define en la norma para que el destructor por defecto no virtual, sino una aplicación añade un destructor virtual, a continuación, un programa portátil podría decir la diferencia [*].
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 les permite tener un destructor virtual.
b) determinar el conjunto de programas que se puede distinguir si type_info
es polimórfico o no, y prohibir a todos. Puede que no sean programas muy útiles o productivas, no sé, pero para su prohibición que tiene que llegar a algún lenguaje estándar que describe la excepción específica que está haciendo a las reglas normales.
Por lo tanto creo que la norma tiene que exigir ya sea el destructor virtual, o prohibirlo. Por lo que es opcional es demasiado complejo (o tal vez debería decir, creo que sería juzgado innecesariamente compleja. Complejidad nunca dejó el comité de estándares en las áreas donde se consideró que vale la pena ...)
Si que fue prohibida, sin embargo, a continuación, una aplicación podría:
- añadir un destructor virtual en cierta clase derivada de
type_info
- derivar toda su typeinfo objetos de que class
- usar que internamente como la clase base polimórfica por todo
que resolvería la situación que he descrito en la parte superior del poste, y el tipo estático de una expresión typeid
seguiría siendo const std::type_info
, por lo que sería difícil para implementaciones para definir extensiones donde los programas pueden dynamic_cast
a varios objetivos para ver qué tipo de objeto type_info
que tienen en un caso particular. Tal vez el estándar de espera para permitir que, a pesar de una implementación siempre podía ofrecer una variante de typeid
con un tipo estático diferente, o garantía de que un static_cast
a una determinada clase de extensión va a 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 más que cualquier cosa que no sería pasar el tiempo preguntándose por qué está allí; -)
[*] En realidad, no he demostrado que. He demostrado que un programa ilegal sería, todo ser lo demás igual, compilación. Sin embargo, una aplicación podría tal vez resolver esto para que no todos son iguales, y que no se compilará. is_polymorphic
de impulso no es portátil, lo que si bien es posible que un programa de prueba de que una clase es polimórfica, que debería ser, puede no haber camino para un programa conforme a la prueba de que una clase no es polimórfica, que no debe ser. Sin embargo, creo que incluso si es imposible, lo que demuestra que, con el fin de eliminar una línea de la norma, es bastante mucho esfuerzo.
Otros consejos
El C ++ estándar dice que typeid
devuelve un objeto de tipo type_info, OR AN definido por la implementación subclase de los mismos. Así que ... supongo que esto es más o menos la respuesta. Así que no veo por qué se rechaza sus puntos 1 y 2.
El párrafo 5.2.8 Cláusula 1 de la corriente de C ++ estándar lee:
El resultado de una expresión typeid es una lValue de const tipo estático std :: type_info (18.5.1) y dinámico tipo const std :: type_info o const nombre donde nombre es una clase de implementación definidos deriva de std :: type_info que conserva el comportamiento descrito en 18.5.1.61) El tiempo de vida del objeto a que se refiere por el lvalue se extiende hasta el final de el programa. Sea o no el destructor se invoca para la type_info objeto al final del programa es sin especificar.
Lo que a su vez significa que uno puede escribir el siguiente código es legal y fino:
const type_info& x = typeid(expr);
que puede requerir que type_info ser polimórficos
Sobre el más simple id "global" que puede tener en C ++ es un nombre de clase, y typeinfo
proporciona una manera de comparar tales Identificación de la igualdad. Pero el diseño es tan torpe y limitado que este caso es necesario typeinfo
envoltura de alguna clase de contenedor, por ejemplo, para ser capaz de poner los casos en colecciones. Andrei Alexandrescu hizo que en su "Modern C ++ Diseño" y creo que ese envoltorio typeinfo
es parte de la biblioteca Loki; es probable que haya uno también en Boost; y es bastante fácil de rodar su propia, por ejemplo, ver mi propio envoltorio .
Pero incluso para una envoltura tales que no hay, en general, cualquier necesidad de un destructor virtual en typeinfo
.
La pregunta, por tanto, no es tanto "eh, ¿por qué hay un destructor virtual", sino más bien, como yo lo veo, "eh, ¿por qué es el diseño de modo hacia atrás, torpe y no directamente utilizable"? Y me deja eso al proceso de normalización. Por ejemplo, iostreams no son precisamente ejemplos de diseño excelente, ya sea; no algo para emular.
3 / deja abierta la posibilidad de metaclases con tipos personalizados - cada
class A
polimórfica real sería conseguir unaA__type_info
derivada "metaclase", que deriva detype_info
. Quizás tales clases derivadas pueden exponer a los miembros quenew A
llamada con diversos argumentos del constructor en una forma de tipo seguro, y cosas por el estilo. Pero haciéndose polimórficatype_info
realidad hace que tal idea básicamente imposible de implementar, ya que tendría que tener metaclases para sus metaclases, hasta el infinito, que es un problema si todos los objetostype_info
tienen una duración de almacenamiento estático. Tal vez Salvo esto es la razón de lo que es polimórfica.
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í