'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.