'typeid' versus 'typeof' in C ++


158

Ich frage mich, was der Unterschied zwischen typeidund typeofin C ++ ist. Folgendes weiß ich:

Außerdem habe ich hier einen Testcodetest erstellt, bei dem ich festgestellt habe, dass er typeidnicht das zurückgibt, was ich erwartet hatte. Warum?

main.cpp

#include <iostream>  
#include <typeinfo>  //for 'typeid' to work  

class Person {  
    public:
    // ... Person members ...  
    virtual ~Person() {}  
};  

class Employee : public Person {  
    // ... Employee members ...  
};  

int main () {  
    Person person;  
    Employee employee;  
    Person *ptr = &employee;  
    int t = 3;  

    std::cout << typeid(t).name() << std::endl;  
    std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)  
    std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)  
    std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)  
    std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time  
                                                       // because it is the dereference of a pointer
                                                       // to a polymorphic class)  
 }  

Ausgabe:

bash-3.2$ g++ -Wall main.cpp -o main  
bash-3.2$ ./main   
i  
6Person  
8Employee  
P6Person  
8Employee

8
Inwiefern druckt Ihr Code Ihrer Meinung nach nicht die richtigen Typnamen? Es sieht gut aus für mich. Die tatsächlich zurückgegebene Zeichenfolge name()ist implementierungsdefiniert. Es muss kein gültiger C ++ - Bezeichnername sein, sondern nur etwas , das den Typ eindeutig identifiziert. Ihre Implementierung verwendet anscheinend das allgemeine Name-Mangling-Schema des Compilers.
Rob Kennedy

Danke Rob! Ich hatte genau diese Typnamen erwartet, wie ich sie in en.wikipedia.org/wiki/Typeid gesehen habe. Was kann Name-Mangling hier tun?
Tim

Wenn Sie neu in typeid sind wie ich: Sie benötigen eine virtuelle Funktion im Basistyp, um die vtable zu aktivieren, oder die letzte Zeile gibt den Basistyp aus.
jw_

Antworten:


198

In der C ++ - Sprache gibt es keine typeof. Sie müssen sich eine compilerspezifische Erweiterung ansehen. Wenn Sie über GCCs sprechen typeof, ist eine ähnliche Funktion in C ++ 11 über das Schlüsselwort vorhanden decltype. Auch hier hat C ++ kein solches typeofSchlüsselwort.

typeidist ein C ++ - Sprachoperator, der zur Laufzeit Informationen zur Typidentifikation zurückgibt. Grundsätzlich wird ein type_infoObjekt zurückgegeben, das mit anderen type_infoObjekten gleichwertig ist.

Beachten Sie, dass die einzige definierte Eigenschaft des zurückgegebenen type_infoObjekts darin besteht, dass es gleich und nicht type_infogleich ist , dh Objekte, die verschiedene Typen beschreiben, müssen ungleich verglichen werden, während type_infoObjekte, die denselben Typ beschreiben, gleich vergleichen müssen. Alles andere ist implementierungsdefiniert. Bei Methoden, die verschiedene "Namen" zurückgeben, wird nicht garantiert, dass sie von Menschen gelesen werden, und es wird auch nicht garantiert, dass sie überhaupt etwas zurückgeben.

Beachten Sie auch, dass das oben Gesagte wahrscheinlich impliziert (obwohl der Standard dies nicht explizit zu erwähnen scheint), dass aufeinanderfolgende Anwendungen typeiddesselben Typs möglicherweise unterschiedliche type_infoObjekte zurückgeben (die natürlich immer noch gleich verglichen werden müssen).


1
Benötigt dies kein Update seit C ++ 11 decltype? Ich bin nicht sicher, was die allgemeine Richtlinie ist, aber da die Frage markiert ist, C++würde ich erwarten, dass sie sich auf den neuesten Standard bezieht. Die Frage neu zu markieren, C++03wäre imho auch eine Option. Ich persönlich bin manchmal ziemlich verwirrt, da ich preC ++ 11 bei der Arbeit verwenden muss und manchmal nicht sicher bin, was "pre11" oder "post11" ist.
idclev 463035818

11
FYI, decltypeist kein Ersatz für typeof. typeoffunktioniert auch bei Typen, während decltypedies nicht der Fall ist. Zum Beispiel typeof(int)ist intwhile decltype(int)ein Fehler.
Shahbaz

1
" type_infoObjekte, die verschiedene Typen beschreiben, müssen ungleich vergleichen" . Dies ist eigentlich nicht garantiert . Der Ungleichungsoperator wurde in C ++ 20 entfernt, um (ich nehme an) davon abzuhalten, sich auf verschiedene Typen zu verlassen, die ungleich vergleichen. Aber wenn Sie darüber nachdenken, ist Gleichheit nicht sicher, wenn Ungleichheit nicht sicher ist.
Indiana Kernick

50

Der Hauptunterschied zwischen den beiden ist der folgende

  • typeof ist ein Konstrukt zur Kompilierungszeit und gibt den zum Kompilierungszeitpunkt definierten Typ zurück
  • typeid ist ein Laufzeitkonstrukt und gibt daher Informationen über den Laufzeittyp des Werts.

Referenztyp: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

typeid Referenz: https://en.wikipedia.org/wiki/Typeid


Vielen Dank, JaredPar! Ich habe einige neue Fragen im aktualisierten Beitrag, nachdem ich Ihre Antworten gelesen habe. Zum Beispiel, wenn es auch wahr ist, dass ihre Rückgaben für verschiedene Zwecke verwendet werden: Die Rückgabe von typeof wird als Schlüsselwort type verwendet, das eine Variable definieren kann, die Rückgabe von typeid jedoch nicht?
Tim

26

typeidkann zur Laufzeit arbeiten und ein Objekt zurückgeben, das den Laufzeittyp des Objekts beschreibt. Dies muss ein Zeiger auf ein Objekt einer Klasse mit virtuellen Methoden sein, damit RTTI (Laufzeittypinformationen) in der Klasse gespeichert werden können. Es kann auch den Kompilierungszeittyp eines Ausdrucks oder einen Typnamen angeben, wenn kein Zeiger auf eine Klasse mit Laufzeittypinformationen angegeben wird.

typeofist eine GNU-Erweiterung und gibt Ihnen den Typ eines beliebigen Ausdrucks zur Kompilierungszeit an. Dies kann beispielsweise beim Deklarieren temporärer Variablen in Makros hilfreich sein, die für mehrere Typen verwendet werden können. In C ++ verwenden Sie normalerweise stattdessen Vorlagen .


5
Soweit ich weiß, typeidwird jeder Ausdruck akzeptiert, nicht nur diejenigen, die Objekte mit virtuellen Methoden auswerten. Darüber hinaus typeidwird eine Art annehmen Namen , nicht nur einen Ausdruck. Sie können sagen typeid(5)oder typeid(std::string)wenn Sie wollen.
Rob Kennedy

1
Ich habe meine Antwort klargestellt, um dies klar zu machen. typeid kann Informationen zum Laufzeittyp zurückgeben, falls verfügbar, bietet jedoch Informationen zum Kompilierungszeittyp für alles andere.
Brian Campbell

Danke, Brian und Rob! Ich habe einige neue Fragen im aktualisierten Beitrag, nachdem ich Ihre Antworten gelesen habe.
Tim

22

Beantwortung der Zusatzfrage:

Mein folgender Testcode für typeid gibt nicht den richtigen Typnamen aus. Was ist los mit dir?

Es ist nichts falsch. Was Sie sehen, ist die Zeichenfolgendarstellung des Typnamens. Das Standard-C ++ zwingt Compiler nicht dazu, den genauen Namen der Klasse auszugeben. Es ist nur Sache des Implementierers (Compiler-Hersteller), zu entscheiden, was geeignet ist. Kurz gesagt, die Namen liegen beim Compiler.


Dies sind zwei verschiedene Werkzeuge. typeofGibt den Typ eines Ausdrucks zurück, ist jedoch kein Standard. In C ++ 0x gibt es etwas, decltypedas den gleichen Job AFAIK macht.

decltype(0xdeedbeef) number = 0; // number is of type int!
decltype(someArray[0]) element = someArray[0];

Während typeidbei polymorphen Typen verwendet wird. Zum Beispiel kann sagen , dass catherleitet animal:

animal* a = new cat; // animal has to have at least one virtual function
...
if( typeid(*a) == typeid(cat) )
{
    // the object is of type cat! but the pointer is base pointer.
}

Danke, Arak! Ich habe den Beitrag gerade mit einigen neuen Fragen aktualisiert. Bitte schauen Sie wenn möglich.
Tim

4

typeid gibt den Typ der Daten zur Laufzeit an, wenn Sie dazu aufgefordert werden. Typedef ist ein Konstrukt zur Kompilierungszeit, das einen neuen Typ definiert, wie danach angegeben. Es gibt keinen Typ in C ++. Die Ausgabe wird wie folgt angezeigt (angezeigt als eingeschriebene Kommentare):

std::cout << typeid(t).name() << std::endl;  // i
std::cout << typeid(person).name() << std::endl;   // 6Person
std::cout << typeid(employee).name() << std::endl; // 8Employee
std::cout << typeid(ptr).name() << std::endl;      // P6Person
std::cout << typeid(*ptr).name() << std::endl;     //8Employee

3

Sie können Boost Demangle verwenden, um einen gut aussehenden Namen zu erhalten:

#include <boost/units/detail/utility.hpp>

und so ähnlich

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.