2010-05-20 18 views
4

Je voulais vérifier que typeid est évalué à la compilation lorsqu'il est utilisé avec un nom de type (ie typeid (int), typeid (std :: string) ...). Pour ce faire, j'ai répété en boucle la comparaison de deux appels de type, et je l'ai compilé avec les optimisations activées, afin de voir si le compilateur a simplifié la boucle (en regardant le temps d'exécution qui est 1us quand il simplifie au lieu de 160ms quand ce n'est pas le cas).Est-ce que typeid de type name est toujours évalué à la compilation en C++?

Et j'obtiens des résultats étranges, parce que parfois le compilateur simplifie le code, et parfois il ne le fait pas. J'utilise g ++ (j'ai essayé différentes versions 4.x), et voici le programme:

#include <iostream> 
#include <typeinfo> 
#include <time.h> 

class DisplayData {}; 

class RobotDisplay: public DisplayData {}; 
class SensorDisplay: public DisplayData {}; 

class RobotQt {}; 
class SensorQt {}; 

timespec tp1, tp2; 
const int n = 1000000000; 

int main() 
{ 
    int avg = 0; 
    clock_gettime(CLOCK_REALTIME, &tp1); 
    for(int i = 0; i < n; ++i) 
    { 
//  if (typeid(RobotQt) == typeid(RobotDisplay)) // (1) compile time 
//  if (typeid(SensorQt) == typeid(SensorDisplay)) // (2) compile time 
     if (typeid(RobotQt) == typeid(RobotDisplay) || 
      typeid(SensorQt) == typeid(SensorDisplay)) // (3) not compile time ???!!! 
      avg++; 
     else 
      avg--; 
    } 
    clock_gettime(CLOCK_REALTIME, &tp2); 
    std::cout << "time (" << avg << "): " << 
     (tp2.tv_sec-tp1.tv_sec)*1000000000+(tp2.tv_nsec-tp1.tv_nsec) << 
     " ns" << std::endl; 
} 

Les conditions dans lesquelles ce problème apparaissent ne sont pas claires, mais:
- s'il n'y a pas d'héritage en cause, pas problème (toujours la compilation)
- si je fais une seule comparaison, aucun problème
- le problème n'apparaît qu'avec une disjonction des comparaisons si toutes les conditions sont fausses

donc, il y a quelque chose que je n'ai pas avec le fonctionnement de typeid (est-il toujours supposé être évalué lors de la compilation lorsqu'il est utilisé avec le type nam es?) ou peut-il s'agir d'un bug gcc en évaluation ou en optimisation? En ce qui concerne le contexte, j'ai localisé le problème dans cet exemple très simplifié, mais mon but est d'utiliser typeid avec les types de modèles (car la spécialisation du modèle de fonction partielle n'est pas possible).

Merci pour votre aide!

+0

Est-ce que vous basez votre conclusion entièrement sur la durée de l'exécution de votre code, ou avez-vous une preuve plus définitive de ce que le compilateur produit réellement? –

+0

Pouvez-vous concevoir votre programme sans avoir besoin de 'typeid'? Un programme qui compare les types d'objets est considéré comme un programme OO mal formé. –

+0

Dennis> Oui, mais je viens de vérifier le code asm et je peux confirmer que dans un cas il y a 6 instructions sans saut entre les deux appels clock_gettime, et dans l'autre cas 15 instructions avec 2 sauts incluant une boucle évidente. – cyril42e

Répondre

7

Je ne connais pas vraiment la réponse à votre question, mais si vous utilisez is_same <> metafunction au lieu de typeid vous pourriez obtenir des résultats plus désirables. Même si vous n'avez pas accès à ce métafonction, il est très facile d'écrire un:


template < typename T1, typename T2 > 
struct is_same 
{ 
    enum { value = false }; // is_same represents a bool. 
    typedef is_same<T1,T2> type; // to qualify as a metafunction. 
}; 

template < typename T > 
struct is_same 
{ 
    enum { value = true }; 
    typedef is_same<T,T> type; 
}; 
+0

Merveilleux! Cependant, je dû modifier un peu la fonction pour travailler: [template struct is_same ] [if (is_same :: value) {}] Et il y a un boost :: is_same. Merci beaucoup, je le ferai! – cyril42e

2

typeid fait partie du mécanisme Run-Time Type d'identification, ce qui suggère ce qu'il est utile pour: il est l'utilisation principale est identifier le type dynamique d'un pointeur/référence à une classe de base au moment de l'exécution. Lorsque les types sont statiquement connus au moment de la compilation, vous n'avez pas besoin de les "identifier" car vous savez déjà ce qu'ils sont. Dans l'exemple, il n'y a rien à identifier au moment de l'exécution, mais les résultats ne sont pas utiles à la compilation (typeid ne peut pas apparaître dans const-expressions, ce qui est nécessaire pour la métaprogrammation de template).

Je vous recommande donc aussi is_same

+0

Oui je suis d'accord, je ne connaissais pas is_same, mais c'est bien ce que je veux. Merci! – cyril42e

2

Pour tout type T, si T est polymorphique, le compilateur est nécessaire d'évaluer les choses typeid lors de l'exécution. Si T est non-polymorphe, le compilateur est requis pour évaluer la substance typeid au moment de la compilation. Cependant, je ne peux pas trouver la référence pertinente dans le brouillon C++ (n3000.pdf) pour cela.

En fait, dans l'un des projets sur lesquels j'ai travaillé, cette astuce a été utilisée pour trouver si une classe était polymorphe à l'exécution.

template <class T> 
bool isPolymorphic() { 
    bool answer=false; 
    T *t = new T(); 
    typeid(answer=true,*t); 
    delete t; 
    return answer; 
} 

j'avais posé une question connexe here sur SO il y a quelques mois.

+1

Ok, mais vous utilisez typeid sur un objet, pas un nom de type. Même si le type est polymorphe, étant donné le nom du type, il est complètement connu au moment de la compilation, n'est-ce pas? – cyril42e

+0

À quoi bon faire 'new' /' delete' au lieu de simplement déclarer un objet local? – AnT

+0

@AndreyT: Tout ce qui donne 'lvalue' serait utile à l'opérateur typeid. Je viens de mettre la réponse de l'autre thread pour l'exhaustivité. – Abhay