'typeid' versus 'typeof' en C ++

159

Me pregunto cuál es la diferencia entre typeidy typeofen C ++. Esto es lo que sé:

  • typeidse menciona en la documentación de type_info que se define en el archivo de encabezado C ++ typeinfo .

  • typeofse define en la extensión GCC para C y en la biblioteca Boost de C ++ .

Además, aquí está la prueba de código de prueba que he creado donde descubrí que typeidno devuelve lo que esperaba. ¿Por qué?

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

salida:

bash-3.2$ g++ -Wall main.cpp -o main  
bash-3.2$ ./main   
i  
6Person  
8Employee  
P6Person  
8Employee
Tim
fuente
8
¿De qué manera crees que tu código no imprime los nombres de tipo correctos? Se ve bien para mi. La cadena real devuelta por name()está definida por la implementación. No tiene que ser un nombre de identificador de C ++ válido, solo algo que identifique de forma exclusiva el tipo. Parece que su implementación utiliza el esquema general de cambio de nombre del compilador.
Rob Kennedy el
Gracias Rob! Esperaba los mismos nombres de tipo que vi en en.wikipedia.org/wiki/Typeid. ¿Qué puede hacer el cambio de nombre aquí?
Tim
Si es nuevo en typeid como yo: necesita una función virtual en el tipo base para encender la tabla virtual o la última línea imprimirá el tipo base.
jw_

Respuestas:

200

El lenguaje C ++ no tiene tal cosa como typeof. Debe estar mirando alguna extensión específica del compilador. Si está hablando de GCC typeof, entonces una característica similar está presente en C ++ 11 a través de la palabra clave decltype. De nuevo, C ++ no tiene esa typeofpalabra clave.

typeides un operador de lenguaje C ++ que devuelve información de identificación de tipo en tiempo de ejecución. Básicamente devuelve un type_infoobjeto, que es comparable a la igualdad con otros type_infoobjetos.

Tenga en cuenta que la única propiedad definida del type_infoobjeto devuelto es que es comparable a la igualdad y no a la igualdad, es decir, los type_infoobjetos que describen diferentes tipos deben comparar no iguales, mientras que los type_infoobjetos que describen el mismo tipo tienen que comparar iguales. Todo lo demás está definido por la implementación. No se garantiza que los métodos que devuelven varios "nombres" devuelvan nada legible para los humanos, e incluso no se garantiza que devuelvan nada en absoluto.

Tenga en cuenta también que lo anterior probablemente implica (aunque el estándar no parece mencionarlo explícitamente) que las aplicaciones consecutivas typeiddel mismo tipo podrían devolver diferentes type_infoobjetos (que, por supuesto, todavía tienen que comparar iguales).

Hormiga
fuente
1
¿no necesitaría una actualización ya que C ++ 11 tiene decltype? No estoy seguro de cuál es la política general, pero como la pregunta está etiquetada C++, esperaría que se refiera al último estándar. Volver a etiquetar la pregunta como C++03también sería una opción en mi humilde opinión. Personalmente, a veces me confundo bastante, ya que tengo que usar preC ++ 11 en el trabajo y a veces no estoy seguro de lo que es verdadero "pre11" o "post11".
idclev 463035818
11
Para su información, decltypeno es un reemplazo para typeof. typeoffunciona en tipos también mientras decltypeno lo hace. Por ejemplo, typeof(int)es intwhile decltype(int)es un error.
Shahbaz
1
"los type_infoobjetos que describen diferentes tipos compararán no iguales" . En realidad, esto no está garantizado . El operador de desigualdad se eliminó en C ++ 20 para (supongo) desalentar la dependencia de diferentes tipos de comparación no igual. Pero si lo piensas bien, la igualdad no es segura si la desigualdad no es segura.
Indiana Kernick el
51

La principal diferencia entre los dos es la siguiente

  • typeof es una construcción en tiempo de compilación y devuelve el tipo definido en tiempo de compilación
  • typeid es una construcción en tiempo de ejecución y, por lo tanto, proporciona información sobre el tipo de tiempo de ejecución del valor.

Tipo de referencia: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

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

JaredPar
fuente
¡Gracias JaredPar! Tengo algunas preguntas nuevas en la publicación actualizada después de leer sus respuestas. Por ejemplo, si también es cierto que sus retornos se usan para diferentes propósitos: ¿el retorno de typeof se usa como palabra clave de tipo que puede definir variable, pero el retorno de typeid no?
Tim
26

typeidpuede operar en tiempo de ejecución y devolver un objeto que describe el tipo de tiempo de ejecución del objeto, que debe ser un puntero a un objeto de una clase con métodos virtuales para que RTTI (información de tipo de tiempo de ejecución) se almacene en la clase. También puede dar el tipo de tiempo de compilación de una expresión o un nombre de tipo, si no se le da un puntero a una clase con información de tipo de tiempo de ejecución.

typeofes una extensión de GNU y le da el tipo de cualquier expresión en tiempo de compilación. Esto puede ser útil, por ejemplo, para declarar variables temporales en macros que pueden usarse en múltiples tipos. En C ++, generalmente usaría plantillas en su lugar.

Brian Campbell
fuente
55
Hasta donde sé, typeidaceptará cualquier expresión, no solo aquellas que evalúen objetos con métodos virtuales. Además, typeidaceptará un nombre de tipo , no solo una expresión. Puedes decir typeid(5)o typeid(std::string)si quieres.
Rob Kennedy el
1
He aclarado mi respuesta para aclarar eso; typeid puede devolver información de tipo de tiempo de ejecución si está disponible, pero proporcionará información de tipo de tiempo de compilación para cualquier otra cosa.
Brian Campbell
¡Gracias Brian y Rob! Tengo algunas preguntas nuevas en la publicación actualizada después de leer sus respuestas.
Tim
22

Respondiendo la pregunta adicional:

mi siguiente código de prueba para typeid no muestra el nombre de tipo correcto. que pasa

No hay nada malo. Lo que ves es la representación de cadena del nombre del tipo. El estándar C ++ no obliga a los compiladores a emitir el nombre exacto de la clase, solo depende del implementador (proveedor del compilador) decidir qué es adecuado. En resumen, los nombres dependen del compilador.


Estas son dos herramientas diferentes. typeofdevuelve el tipo de una expresión, pero no es estándar. En C ++ 0x hay algo llamado decltypeque hace el mismo trabajo AFAIK.

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

Mientras que typeidse usa con tipos polimórficos. Por ejemplo, digamos que catderiva 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
fuente
¡Gracias Arak! Acabo de actualizar la publicación con algunas preguntas nuevas. Por favor, eche un vistazo si es posible.
Tim
4

typeid proporciona el tipo de datos en tiempo de ejecución, cuando se solicita. Typedef es una construcción en tiempo de compilación que define un nuevo tipo como se indica después de eso. No hay typeof en C ++ La salida aparece como (se muestra como comentarios inscritos):

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
fuente
3

Puede usar Boost demangle para lograr un nombre bonito:

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

y algo como

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