„typeid” kontra „typeof” w C ++

159

Zastanawiam się, jaka jest różnica między typeidiw typeofC ++. Oto co wiem:

  • typeidjest wspomniana w dokumentacji dla type_info, która jest zdefiniowana w pliku nagłówkowym C ++ typeinfo .

  • typeofjest zdefiniowany w rozszerzeniu GCC dla języka C oraz w bibliotece C ++ Boost .

Tutaj jest również test kodu testowego, który stworzyłem, w którym odkryłem, że typeidnie zwraca tego, czego się spodziewałem. Czemu?

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)  
 }  

wynik:

bash-3.2$ g++ -Wall main.cpp -o main  
bash-3.2$ ./main   
i  
6Person  
8Employee  
P6Person  
8Employee
Tim
źródło
8
Jak myślisz, w jaki sposób twój kod nie drukuje prawidłowych nazw typów? Dla mnie to wygląda dobrze. Rzeczywisty ciąg zwracany przez name()jest zdefiniowany przez implementację. Nie musi to być poprawna nazwa identyfikatora C ++, po prostu coś , co jednoznacznie identyfikuje typ. Wygląda na to, że Twoja implementacja używa ogólnego schematu zmiany nazw kompilatora.
Rob Kennedy
Dzięki Rob! Spodziewałem się dokładnie takich samych nazw, jak nazwy typów, które widziałem w en.wikipedia.org/wiki/Typeid. Co może tu zrobić zniekształcanie nazw?
Tim
Jeśli dopiero zaczynasz pisać id, tak jak ja: Potrzebujesz funkcji wirtualnej w typie podstawowym, aby włączyć tabelę vtable lub ostatnia linia wypisze typ podstawowy.
jw_

Odpowiedzi:

200

Język C ++ nie ma czegoś takiego jak typeof. Musisz patrzeć na jakieś rozszerzenie specyficzne dla kompilatora. Jeśli mówisz o GCC typeof, to podobna funkcja jest obecna w C ++ 11 poprzez słowo kluczowe decltype. Ponownie, C ++ nie ma takiego typeofsłowa kluczowego.

typeidjest operatorem języka C ++, który zwraca informacje identyfikujące typ w czasie wykonywania. W zasadzie zwraca type_infoobiekt, który jest porównywalny pod względem równości z innymi type_infoobiektami.

Zauważ, że jedyną zdefiniowaną właściwością zwracanego type_infoobiektu jest to, że jest on porównywalny z równością i nierównością, tj. type_infoObiekty opisujące różne typy powinny porównywać różne typy, a type_infoobiekty opisujące ten sam typ muszą porównywać równe. Wszystko inne jest zdefiniowane w ramach implementacji. Metody, które zwracają różne „nazwy” nie gwarantują zwrócenia niczego czytelnego dla człowieka, a nawet nie gwarantują, że w ogóle zwrócą cokolwiek.

Zauważ również, że powyższe prawdopodobnie implikuje (chociaż norma nie wydaje się o tym wyraźnie mówić), że kolejne aplikacje typeidtego samego typu mogą zwracać różne type_infoobiekty (które oczywiście nadal muszą porównywać równe).

Mrówka
źródło
1
czy nie wymagałoby to aktualizacji, ponieważ C ++ 11 ma decltype? Nie jestem pewien, jakie są ogólne zasady, ale ponieważ pytanie zostało otagowane C++, spodziewam się, że będzie odnosić się do najnowszego standardu. Ponowne oznaczenie pytania jako C++03opcja imho. Osobiście czasami jestem trochę zdezorientowany, ponieważ muszę używać preC ++ 11 w pracy, a czasami nie jestem pewien, co jest prawdą „pre11” lub „post11”.
idclev 463035818
11
FYI, decltypenie zastępuje typeof. typeofdziała również na typach, a decltypenie. Na przykład typeof(int)to intwhile decltype(int)jest błędem.
Shahbaz
1
type_infoobiekty opisujące różne typy porównują nierówności” . Właściwie to nie jest gwarantowane . Operator nierówności został usunięty w C ++ 20, aby (zakładam) zniechęcić do polegania na różnych typach porównujących nierówności. Ale jeśli się nad tym zastanowić, równość nie jest bezpieczna, jeśli nierówność nie jest bezpieczna.
Indiana Kernick
51

Podstawowa różnica między nimi jest następująca

  • typeof jest konstrukcją czasu kompilacji i zwraca typ zdefiniowany w czasie kompilacji
  • typeid to konstrukcja środowiska uruchomieniowego, która zawiera informacje o typie wartości w czasie wykonywania.

odniesienie do typu: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

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

JaredPar
źródło
Dziękuję, JaredPar! Po przeczytaniu twoich odpowiedzi mam kilka nowych pytań w zaktualizowanym poście. Na przykład, jeśli prawdą jest również, że ich zwroty są używane do różnych celów: zwrot typeof jest używany jako słowo kluczowe type, które może definiować zmienną, ale powrót typeid nie może?
Tim
26

typeidmoże działać w czasie wykonywania i zwracać obiekt opisujący typ obiektu w czasie wykonywania, który musi być wskaźnikiem do obiektu klasy z metodami wirtualnymi, aby RTTI (informacje o typie czasu wykonywania) mogły być przechowywane w klasie. Może również podać typ czasu kompilacji wyrażenia lub nazwę typu, jeśli nie zostanie podany wskaźnik do klasy z informacją o typie czasu wykonywania.

typeofjest rozszerzeniem GNU i podaje typ dowolnego wyrażenia w czasie kompilacji. Może to być przydatne na przykład przy deklarowaniu zmiennych tymczasowych w makrach, które mogą być używane w wielu typach. W C ++ zamiast tego używałbyś szablonów .

Brian Campbell
źródło
5
O ile wiem, typeidzaakceptuje każde wyrażenie, nie tylko te, które oceniają obiekty metodami wirtualnymi. Ponadto typeidzaakceptuje nazwę typu , a nie tylko wyrażenie. Możesz powiedzieć typeid(5)lub typeid(std::string)jeśli chcesz.
Rob Kennedy
1
Wyjaśniłem moją odpowiedź, aby to wyjaśnić; typeid może zwrócić informacje o typie czasu wykonywania, jeśli są dostępne, ale dostarczy informacje o typie czasu kompilacji dla cokolwiek innego.
Brian Campbell
Dziękuję, Brian i Rob! Po przeczytaniu twoich odpowiedzi mam kilka nowych pytań w zaktualizowanym poście.
Tim
22

Odpowiadając na dodatkowe pytanie:

mój następujący kod testowy dla typeid nie wyświetla poprawnej nazwy typu. Co jest nie tak?

Nie ma nic złego. To, co widzisz, jest ciągiem reprezentującym nazwę typu. Standardowy C ++ nie wymusza na kompilatorach emisji dokładnej nazwy klasy, tylko implementator (dostawca kompilatora) decyduje, co jest odpowiednie. Krótko mówiąc, nazwy zależą od kompilatora.


To są dwa różne narzędzia. typeofzwraca typ wyrażenia, ale nie jest to standardowe. W C ++ 0x jest coś, co nazywa decltypesię AFAIK.

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

Natomiast typeidjest używany z typami polimorficznymi. Na przykład, powiedzmy, że catwyprowadza 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.
}
AraK
źródło
Dziękuję Araku! Właśnie zaktualizowałem post o kilka nowych pytań. Proszę spojrzeć, jeśli to możliwe.
Tim
4

typeid zapewnia typ danych w czasie wykonywania, gdy zostaniesz o to poproszony. Typedef to konstrukcja czasu kompilacji, która definiuje nowy typ, jak podano później. Nie ma typeof w C ++ Wyjście pojawia się jako (pokazane jako wpisane komentarze):

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
Dr. Debasish Jana
źródło
3

Możesz użyć demangle Boost, aby uzyskać ładnie wyglądającą nazwę:

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

i coś takiego

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Patrick K.
źródło