Question

Je sais que les compilateurs ont beaucoup de liberté dans la mise en œuvre du comportement des fonctions std::type_info.

Je pense à l'utiliser pour comparer les types d'objets, donc je voudrais être sûr que:

  1. std::type_info::name doit retourner deux chaînes différentes pour deux types différents.

  2. std::type_info::before doit dire que Type1 est avant Type2 exclusif ou Type2 est avant Type1.

    // like this:
    typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
    
  3. Deux spécialisation différente de la même classe de modèle sont considérés comme différents types.

  4. Deux typedef-initions du même type sont du même type.

Et enfin:

  • Depuis std::type_info n'est pas copiable, comment pourrais-je stocker quelque part type_infos (par exemple: dans un std::map)? La seule façon d'avoir un std::type_info quelque part toujours alloué (par exemple: sur la pile ou sur une statique / variable globale)? Et utiliser un pointeur vers elle

  • Comment sont rapides operator==, operator!= et before sur la plupart des compilateurs communs? Je suppose qu'ils ne devraient comparer une valeur. Et comment est rapide typeid?

  • J'ai un A de classe avec un virtual bool operator==( const A& ) const. Depuis A a obtenu de nombreuses sous-classes (qui sont inconnus certaines au moment de la compilation), je surcharger cet opérateur virtuel dans toute sous-classe B cette façon:

    virtual bool operator==( const A &other ) const {
      if( typeid(*this) != typeid(other) ) return false;
      // bool B::operator==( const B &other ) const // is defined for any class B
      return operator==( static_cast<B&>( other ) );
    }
    

    Est-ce une manière acceptable (et standard) pour mettre en œuvre cet opérateur?

Était-ce utile?

La solution

Après un rapide coup d'oeil à la documentation, je dirais que:

  1. std :: type_info :: nom renvoie toujours deux chaînes différentes pour deux types différents, sinon cela signifie que le compilateur lui-même perdu tout en résolvant les types et vous ne devriez pas l'utiliser plus.

  2. Référence dit: « avant le retour true si le type précède le type de rhs dans l'ordre de classement L'ordre de classement est juste un ordre interne maintenu par une mise en œuvre particulière et ne sont pas nécessairement liés aux relations d'héritage ou ordonnance déclarant. « . Vous avez donc la garantie qu'aucun type a le même rang dans l'ordre de classement.

  3. Chaque instanciation d'une classe de modèle est un type différent. La spécialisation ne font aucune exception.

  4. Je ne comprends pas vraiment ce que vous voulez dire. Si vous quelque chose comme moyen d'avoir typedef foo bar; dans deux unités de compilation séparée et que la barre est la même dans les deux, cela fonctionne de cette façon. Si vous typedef foo bar; typedef int bar; moyen, il ne fonctionne pas (sauf si foo est int).

A propos de vos questions:

  • Vous devriez stocker des références à std :: type_info, d'emballage en quelque sorte.
  • Absolument aucune idée sur la performance, je suppose que les opérateurs de comparaison ont le temps constant malgré la complexité du type. Avant doit avoir la complexité linéaire en fonction du nombre de différents types utilisés dans votre code.
  • Ceci est vraiment étrange IMHO. Vous devez surcharger votre operator== au lieu de faire ce virtuel et la remplacer.

Autres conseils

Standard 18.5.1 (classe type_info):

  

La type_info classe décrit le type   les informations générées par le   la mise en oeuvre. Les objets de cette classe   stocker efficacement un pointeur vers un nom   pour le type, et une valeur codée   adapté pour comparer deux types de   égalité ou ordre de classement.   noms, règle le codage et le collationnement   séquence de types sont tous non précisée   et peuvent différer entre les programmes .

De ma compréhension:

  1. Vous ne disposez pas de cette garantie en ce qui concerne std:type_info::name. La norme indique seulement que les rendements name un BNT dépendant de l'implémentation , et je crois une mise en œuvre conforme pourrait très bien retourner la même chaîne pour chaque type.
  2. Je ne sais pas, et la norme n'est pas clair sur ce point, je ne voudrais pas compter sur un tel comportement.
  3. Que l'on devrait y avoir un certain « Oui » pour moi
  4. Que l'on devrait y avoir un certain « Oui » pour moi

En ce qui concerne la deuxième série de questions:

Vous pouvez enregistrer comme ça.

class my_type_info
{
public:
     my_type_info(const std::type_info& info) : info_(&info){}
     std::type_info get() const { return *info_;}
private:
     const std::type_info* info_;
};

EDIT:

C de norme 5.2.8.

  

Le résultat d'une   typeid expression est une lvalue de   type statique const std :: type_info ...

Ce qui signifie que vous pouvez l'utiliser comme ceci.

my_type_info(typeid(my_type));

La fonction typeid retourne une lvalue (il n'est pas temporaire) et donc l'adresse du retour type_info est toujours valide.

Les réponses actuelles aux questions 1 et 2 sont parfaitement correctes, et ils sont essentiellement juste des détails pour la classe type_info -. Inutile de répéter ces réponses

Pour des questions 3 et 4, il est important de comprendre ce qui est précisément un type en C ++, et comment ils se rapportent à des noms. Pour commencer, il y a un tas de types prédéfinis, et ceux qui ont des noms: int, float, double. Ensuite, il y a des types construits qui ne pas ont des noms de leurs propres: const int, int*, const int*, int* const. Il existe plusieurs types de fonction int (int) et types pointeur de fonction int (*)(int).

Il est parfois utile de donner un nom à un type sans nom, ce qui est possible en utilisant typedef. Par exemple, typedef int* pint ou typedef int (*pf)(int);. Cela introduit un nom, pas un nouveau type.

Viennent ensuite les types définis par l'utilisateur: struct, les classes, les syndicats. Il y a une bonne convention pour leur donner des noms, mais ce n'est pas obligatoire. Ne pas ajouter le nom d'un tel avec typedef, vous pouvez le faire directement: struct Foo { }; au lieu de typedef struct {} Foo;. Il est fréquent d'avoir des définitions de classe en-têtes, qui finissent par \ dans plusieurs unités de traduction. Cela ne signifie la classe est définie plus d'une fois. Ceci est toujours le même type, et donc vous n'êtes pas autorisé à jouer des tours avec des macros pour modifier les définitions membres de la classe.

Une classe de modèle est pas un type, il est une recette pour les types. Deux instanciation d'un modèle unique de classe sont des types distincts si les arguments de modèle sont différents types (ou valeurs). Cela fonctionne de manière récursive. Compte tenu template <typename T> struct Foo{};, Foo<Foo<int> > est du même type que Foo<Foo<Bar> > si et seulement si Bar est un autre nom pour le int de type

type_info est défini par l'implémentation, donc je voudrais vraiment pas compter sur elle. Toutefois, en fonction de mes expériences en utilisant g ++ et MSVC, les hypothèses 1,3 et 4 tiens ... pas vraiment sûr de # 2.

Y at-il une raison quelconque vous ne pouvez pas utiliser une autre méthode comme ça?

template<typename T, typename U>
struct is_same       { static bool const result = false; };

template<typename T>
struct is_same<T, T> { static bool const result = true;  };

template<typename S, typename T>
bool IsSame(const S& s, const T& t) {   return is_same<S,T>::result; }
scroll top