'typeid' contro 'typeof' in C ++


159

Mi chiedo quale sia la differenza tra typeide typeofin C ++. Ecco quello che so:

  • typeidè menzionato nella documentazione per type_info definita nel file di intestazione C ++ typeinfo .

  • typeofè definito nell'estensione GCC per C e nella libreria Boost C ++ .

Inoltre, ecco il test del codice di test che ho creato dove ho scoperto che typeidnon restituisce ciò che mi aspettavo. Perché?

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

produzione:

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

8
In che modo pensi che il tuo codice non stampi i nomi dei tipi giusti? Mi sta bene. La stringa effettiva restituita da name()è definita dall'implementazione. Non deve essere un nome identificativo C ++ valido, solo qualcosa che identifica in modo univoco il tipo. Sembra che l'implementazione utilizzi lo schema generale di modifica del nome del compilatore.
Rob Kennedy,

Grazie Rob! Mi aspettavo esattamente gli stessi nomi dei tipi che ho visto in en.wikipedia.org/wiki/Typeid. Cosa può fare il nome che fa storpiare qui?
Tim

Se sei nuovo di scrivere come me: hai bisogno di una funzione virtuale nel tipo base per accendere la vtable o l'ultima riga stamperà il tipo base.
jw_

Risposte:


200

Il linguaggio C ++ non ha nulla del genere typeof. Devi guardare alcune estensioni specifiche del compilatore. Se stai parlando di GCC typeof, una funzione simile è presente in C ++ 11 tramite la parola chiave decltype. Ancora una volta, C ++ non ha tale typeofparola chiave.

typeidè un operatore di linguaggio C ++ che restituisce informazioni di identificazione del tipo in fase di esecuzione. In pratica restituisce un type_infooggetto, che è paragonabile all'uguaglianza con altri type_infooggetti.

Si noti che l'unica proprietà definita type_infodell'oggetto restituito è la sua comparabilità di uguaglianza e non-uguaglianza, vale a dire che gli type_infooggetti che descrivono tipi diversi devono confrontare non uguali, mentre gli type_infooggetti che descrivono lo stesso tipo devono confrontare uguali. Tutto il resto è definito dall'implementazione. I metodi che restituiscono vari "nomi" non sono garantiti per restituire qualcosa di leggibile dall'uomo, e nemmeno sono garantiti per restituire nulla.

Si noti inoltre che quanto sopra probabilmente implica (sebbene lo standard non sembri menzionarlo esplicitamente) che applicazioni consecutive typeiddello stesso tipo potrebbero restituire type_infooggetti diversi (che, ovviamente, devono ancora confrontare uguali).


1
questo non avrebbe bisogno di un aggiornamento dal C ++ 11 decltype? Non sono sicuro di quale sia la politica generale, ma poiché la domanda è taggata, C++mi aspetto che si riferisca allo standard più recente. Ricodificare la domanda come C++03sarebbe anche un'opzione imho. Personalmente a volte mi confondo abbastanza, dato che devo usare preC ++ 11 al lavoro e talvolta non sono sicuro di cosa sia il vero "pre11" o "post11".
idclev 463035818,

11
Cordiali saluti, decltypenon è un sostituto per typeof. typeoffunziona anche sui tipi mentre decltypeno. Ad esempio, typeof(int)is intwhile decltype(int)is a error.
Shahbaz,

1
"gli type_infooggetti che descrivono tipi diversi devono confrontare non uguali" . In realtà, questo non è garantito . L'operatore di disuguaglianza è stato rimosso in C ++ 20 per (presumo) scoraggiare il fatto di fare affidamento su tipi diversi comparando non uguali. Ma se ci pensate, l'uguaglianza non è sicura se la disuguaglianza non è sicura.
Indiana Kernick il

51

La differenza principale tra i due è la seguente

  • typeof è un costrutto in fase di compilazione e restituisce il tipo come definito in fase di compilazione
  • typeid è un costrutto di runtime e quindi fornisce informazioni sul tipo di runtime del valore.

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

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


Grazie, JaredPar! Ho alcune nuove domande nel post aggiornato dopo aver letto le tue risposte. Come se fosse anche vero che i loro ritorni sono usati per scopi diversi: il ritorno di typeof è usato come parola chiave type che può definire la variabile, ma il ritorno di typeid non può?
Tim

26

typeidpuò funzionare in fase di runtime e restituire un oggetto che descrive il tipo di runtime dell'oggetto, che deve essere un puntatore a un oggetto di una classe con metodi virtuali per poter archiviare RTTI (informazioni sul runtime) nella classe. Può anche fornire il tipo di tempo di compilazione di un'espressione o un nome di tipo, se non viene fornito un puntatore a una classe con informazioni sul tipo di runtime.

typeofè un'estensione GNU e ti dà il tipo di qualsiasi espressione in fase di compilazione. Ciò può essere utile, ad esempio, nel dichiarare variabili temporanee nelle macro che possono essere utilizzate su più tipi. In C ++, in genere si utilizzano invece i modelli .


5
Per quanto ne so, typeidaccetterò qualsiasi espressione, non solo quelle che valutano oggetti con metodi virtuali. Inoltre, typeidaccetterà un nome di tipo , non solo un'espressione. Puoi dire typeid(5)o typeid(std::string)se vuoi.
Rob Kennedy,

1
Ho chiarito la mia risposta per chiarirlo; typeid può restituire informazioni sul tipo di runtime, se disponibili, ma fornirà informazioni sul tipo di tempo di compilazione per qualsiasi altra cosa.
Brian Campbell,

Grazie Brian e Rob! Ho alcune nuove domande nel post aggiornato dopo aver letto le tue risposte.
Tim

22

Rispondere alla domanda aggiuntiva:

il mio codice di test seguente per typeid non genera il nome di tipo corretto. Cosa c'è che non va?

Non c'è niente di sbagliato. Quello che vedi è la rappresentazione in forma di stringa del nome del tipo. Il C ++ standard non obbliga i compilatori ad emettere il nome esatto della classe, spetta solo all'implementatore (fornitore del compilatore) decidere quale sia adatto. In breve, i nomi dipendono dal compilatore.


Questi sono due strumenti diversi. typeofrestituisce il tipo di un'espressione, ma non è standard. In C ++ 0x c'è qualcosa chiamato decltypeche fa lo stesso lavoro AFAIK.

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

Considerando che typeidviene utilizzato con tipi polimorfici. Ad esempio, supponiamo che catderivi 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.
}

Grazie, Arak! Ho appena aggiornato il post con alcune nuove domande. Si prega di dare un'occhiata se possibile.
Tim

4

typeid fornisce il tipo di dati in fase di esecuzione, quando richiesto. Typedef è un costrutto in fase di compilazione che definisce un nuovo tipo come indicato in seguito. Non esiste un tipo di C in Output che appare come (mostrato come commento inscritto):

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

Puoi usare Boost demangle per ottenere un bel nome:

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

e qualcosa del genere

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.