Pourquoi est-std :: type_info polymorphes?
-
28-09-2019 - |
Question
Y at-il une raison pour laquelle std::type_info
est spécifié polymorphes? Le destructor est spécifié comme virtuel (et il y a un commentaire à l'effet de « afin qu'il soit polymorphes » dans la conception et l'évolution de C ++). Je ne vois pas vraiment une raison impérieuse. Je n'ai pas cas d'utilisation spécifique, je me demandais si jamais il y avait une raison ou une histoire derrière elle.
Voici quelques idées que je suis venu avec et rejetais:
- Il est un point d'extension - implémentations peuvent définir des sous-classes, et les programmes pourraient alors essayer de
dynamic_cast
unstd::type_info
à un autre, type dérivé défini par l'implémentation. Ceci est peut-être la raison, mais il semble que c'est tout aussi facile pour les implémentations d'ajouter un membre défini la mise en œuvre, ce qui pourrait être virtuelle. Les programmes qui souhaitent tester ces extensions seraient nécessairement non-portable de toute façon. - Il est de veiller à ce que les types dérivés sont détruits correctement lorsque
delete
ing un pointeur de base. Mais il n'y a pas de types dérivés standard, les utilisateurs ne peuvent pas définir des types dérivés utiles, cartype_info
n'a pas les constructeurs standards publics, et ainsidelete
ing un pointeurtype_info
est jamais légal et portable. Et les types dérivés ne sont pas utiles parce qu'ils ne peuvent pas être construits - l'utilisation que je sais que pour ces types dérivés inconstructible est dans la mise en œuvre des choses comme le trait de typeis_polymorphic
.
- Il laisse ouverte la possibilité de métaclasses avec des types personnalisés - chaque véritable
class A
polymorphes obtiendrait uneA__type_info
« metaclass » dérivé, qui dérive detype_info
. Peut-être que ces classes dérivées pourraient exposer les membres quinew A
d'appel avec divers arguments du constructeur d'une manière de type sécurisé, et des choses comme ça. Mais faire se rend en fait une telle idée polymorphiquetype_info
pratiquement impossible à mettre en œuvre, parce que vous devriez avoir métaclasses pour vos métaclasses, ad infinitum, ce qui est un problème si tous les objetstype_info
ont une durée de stockage statique. barrant peut-être c'est la raison pour le rendre polymorphes. - Il y a une certaine utilisation pour appliquer des fonctionnalités RTTI (autres que
dynamic_cast
) sestd::type_info
, ou quelqu'un a pensé qu'il était mignon, ou gênant sitype_info
était pas polymorphes. Mais étant donné qu'il n'y a pas de type dérivé standard, et pas d'autres classes dans la hiérarchie standard que l'on pourrait raisonnablement essayer de contre-CAST pour, la question est: quoi? Y at-il une utilisation pour des expressions telles quetypeid(std::type_info) == typeid(typeid(A))
? - Il est parce que implémenteurs vont créer leur propre type dérivé privé (comme je crois que GCC fait). Mais, pourquoi prendre la peine de le spécifier? Même si le destructor n'a pas été spécifié comme virtuel et un implémenteur a décidé qu'il devrait être, sans doute que la mise en œuvre pourrait la déclarer virtuelle, parce qu'elle ne change pas l'ensemble des opérations autorisées sur
type_info
, donc un programme portable ne serait pas en mesure de faire la différence. - Il y a quelque chose à voir avec les compilateurs avec ABIs coexistant partiellement compatible, peut-être en raison de l'enchaînement dynamique. Peut-être pourraient reconnaître leur implémenteurs propre sous-classe de
type_info
(par opposition à une provenant d'un autre fournisseur) de manière portable sitype_info
est garanti virtuelle.
Le dernier est le plus plausible pour moi en ce moment, mais il est assez faible.
La solution
Je suppose qu'il est là pour la commodité de mise en œuvre. Il leur permet de définir des classes de type_info
étendues, et les supprimer par des pointeurs vers type_info
à la sortie du programme, sans avoir à construire dans la magie spéciale du compilateur pour appeler le destructor correct, ou sauter à travers des cerceaux autrement.
sûrement que la mise en œuvre pourrait déclarer virtuelle, parce qu'il ne modifier l'ensemble des opérations autorisées sur type_info, donc un programme portable ne serait pas en mesure de dire différence.
Je ne pense pas que ce soit vrai. Considérez ce qui suit:
#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);
}
Que ce soit raisonnable ou non, la première coulée dynamique est autorisée (et Equivaut à un pointeur NULL), tandis que la seconde coulée dynamique n'est pas autorisé. Donc, si type_info
a été défini dans la norme pour que le non-destructor virtuelle par défaut, mais une implémentation a ajouté un destructor virtuel, un programme portable pourrait faire la différence [*].
Il semble plus simple de me mettre le destructeur virtuel dans la norme, que ce soit à:
a) mettre une note dans la norme, bien que la définition de la classe implique que type_info
n'a pas de fonctions virtuelles, il est permis d'avoir une destructor virtuelle.
b) déterminer l'ensemble des programmes qui peuvent distinguer si type_info
est polymorphe ou non, et à les interdire tout. Ils ne peuvent pas être très programmes utiles ou productifs, je ne sais pas, mais de les interdire, vous devez venir avec une langue standard qui décrit l'exception spécifique que vous faites aux règles normales.
Par conséquent, je pense que la norme doit mandater soit le destructor virtuel, ou l'interdire. Rendant facultatif est trop complexe (ou peut-être devrais-je dire, je pense qu'il serait jugé inutilement complexe. La complexité n'a jamais cessé de le Comité des normes dans les domaines où il a été jugé utile ...)
S'il a été interdit, cependant, alors une mise en œuvre pourrait:
- ajouter une destructor virtuelle à une classe dérivée de
type_info
- déduire la totalité de ses typeinfo objets de que class
- l'utilisation qui en interne comme la classe de base polymorphes pour tout
qui résoudrait la situation que je décrivais au sommet du poteau, et type statique d'une expression typeid
serait encore const std::type_info
, il serait difficile pour les implémentations de définir les extensions où les programmes peuvent dynamic_cast
à différentes cibles pour voir quel genre d'objet type_info
qu'ils ont dans un cas particulier. Peut-être la norme espérait permettre que, bien qu'une mise en œuvre pourrait toujours offrir une variante de typeid
avec un autre type statique ou garantie qu'un static_cast
à une certaine classe d'extension fonctionnera, puis laisser le dynamic_cast
du programme à partir de là.
En résumé, pour autant que je sais que le virtuel est potentiellement destructor utile aux initiateurs de projets et le retirer ne gagne rien à personne autre que nous ne serions pas passer du temps se demander pourquoi il est là; -)
[*] En fait, je n'ai pas démontré que. Je l'ai démontré qu'un programme illégal serait, ceteris paribus, compilez. Mais une mise en œuvre pourrait peut-être travailler autour de ce en veillant à ce que tout est pas égal, et qu'il ne compile pas. Le is_polymorphic
Boost n'est pas portable, si bien qu'il est possible pour un programme de test d'une classe polymorphes, qui devrait être, il peut y avoir aucun moyen pour un programme conforme à l'essai qu'une classe est pas polymorphes, cela ne devrait pas être. Je pense cependant que même s'il est impossible, ce qui prouve que, afin d'éliminer une ligne de la norme, est tout à fait beaucoup d'efforts.
Autres conseils
La norme C ++ indique que les rendements de typeid
un objet de type type_info, OU UNE MISE EN OEUVRE définie sous-classe de celle-ci. Alors ... Je suppose que cela est à peu près la réponse. Je ne vois donc pas pourquoi vous rejetez vos points 1 et 2.
Le paragraphe 1 de l'article 5.2.8 de la norme actuelle C ++ lit comme suit:
Le résultat d'une expression est une typeid lvalue de type statique const std :: type_info (18.5.1) et dynamique de type const std :: type_info ou const nom où nom est un classe d'implémentation défini dérivée std :: type_info qui conserve le problème décrit dans 18.5.1.61) La durée de vie de l'objet désigné par la lvalue se prolonge jusqu'à la fin de le programme. Que ce soit ou non destructor est appelé à la type_info objet à la fin du programme est Non spécifié.
qui signifie à son tour que l'on pourrait écrire le code suivant est légal et fin:
const type_info& x = typeid(expr);
qui peut exiger que type_info être polymorphes
A propos de la plus simple id « global » vous pouvez avoir en C ++ est un nom de classe et typeinfo
fournit un moyen de comparer cette id est pour l'égalité. Mais la conception est si maladroit et limité que vous devez ensuite envelopper typeinfo
dans une classe d'emballage, par exemple pour être en mesure de mettre des instances dans les collections. Andrei Alexandrescu a fait dans son « Modern C ++ Design » et je pense que cette enveloppe de typeinfo
fait partie de la bibliothèque Loki; il y a probablement un aussi Boost; et il est assez facile de rouler vos propres, par exemple voir mon wrapper.
Mais même pour un tel emballage il n'y a pas en général besoin d'un destructor virtuel dans typeinfo
.
La question est donc pas tellement « hein, pourquoi est-il un destructeur virtuel », mais plutôt, comme je le vois «hein, pourquoi est la conception si arriérées, maladroit et ne sont pas directement utilisables »? Et je mettrais cela sur le processus de normalisation. Par exemple, iostreams ne sont pas exactement des exemples de conception superbe, que ce soit; pas quelque chose à imiter.
3 / Il laisse ouverte la possibilité de métaclasses avec des types personnalisés - chaque véritable
class A
polymorphes obtiendrait uneA__type_info
« metaclass » dérivé, qui dérive detype_info
. Peut-être que ces classes dérivées pourraient exposer les membres quinew A
d'appel avec divers arguments du constructeur d'une manière de type sécurisé, et des choses comme ça. Mais faire se rend en fait une telle idée polymorphiquetype_info
pratiquement impossible à mettre en œuvre, parce que vous devriez avoir métaclasses pour vos métaclasses, ad infinitum, ce qui est un problème si tous les objetstype_info
ont une durée de stockage statique. barrant peut-être c'est la raison pour le rendre polymorphes.
Clever ...
Quoi qu'il en soit, je suis en désaccord avec ce raisonnement: cette mise en œuvre pourrait facilement exclure une méta classes pour les types dérivés de type_info
, y compris lui-même type_info