'typeid' versus 'typeof' em C ++


158

Eu estou querendo saber qual é a diferença entre typeide typeofem C ++. Aqui está o que eu sei:

  • typeidé mencionado na documentação para type_info, que é definida no arquivo de cabeçalho do C ++, typeinfo .

  • typeofé definido na extensão GCC para C e na biblioteca C ++ Boost .

Além disso, aqui está o teste do código de teste que eu criei onde descobri que typeid não retorna o que eu esperava. 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)  
 }  

resultado:

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

8
De que maneira você acha que seu código não imprime os nomes de tipo corretos? Parece bom para mim. A cadeia real retornada por name()é definida pela implementação. Ele não precisa ser um nome de identificador C ++ válido, apenas algo que identifique exclusivamente o tipo. Parece que sua implementação usa o esquema geral de separação de nomes do compilador.
Rob Kennedy

Obrigado Rob! Eu esperava exatamente os mesmos nomes de tipo que vi em en.wikipedia.org/wiki/Typeid. O que os nomes diferentes podem fazer aqui?
Tim

Se você é iniciante no tipo typeid como eu: Você precisa de uma função virtual no tipo base para ativar a vtable ou a última linha imprimirá o tipo base.
jw_

Respostas:


198

A linguagem C ++ não possui tal coisa typeof. Você deve estar olhando para alguma extensão específica do compilador. Se você está falando sobre GCCs typeof, um recurso semelhante está presente no C ++ 11 por meio da palavra-chave decltype. Novamente, o C ++ não tem taltypeof palavra-chave.

typeidé um operador de linguagem C ++ que retorna informações de identificação de tipo em tempo de execução. Ele basicamente retorna um type_infoobjeto que é comparável à igualdade com outrostype_info objetos.

Observe que a única propriedade definida do type_infoobjeto retornado é ser comparável entre igualdade e não-igualdade, ou seja, type_infoobjetos que descrevem tipos diferentes devem comparar diferentes, enquanto type_infoobjetos que descrevem o mesmo tipo precisam comparar iguais. Tudo o resto é definido pela implementação. Os métodos que retornam vários "nomes" não têm garantia de retornar nada legível por humanos e nem mesmo garantem que retornem nada.

Observe também que o acima provavelmente implica (embora o padrão não pareça mencioná-lo explicitamente) que aplicativos consecutivos typeiddo mesmo tipo possam retornar type_infoobjetos diferentes (que, é claro, ainda precisam ser comparados).


1
isso não precisaria de atualização, já que o C ++ 11 possui decltype? Não sei ao certo qual é a política geral, mas como a pergunta está marcada C++, espero que se refira ao padrão mais recente. Voltar a marcar a pergunta como C++03também seria uma opção imho. Pessoalmente, às vezes fico bastante confuso, pois tenho que usar o preC ++ 11 no trabalho e às vezes não tenho certeza do que é verdadeiro "pre11" ou "post11".
Idclev 463035818

11
Para sua informação, decltypenão é um substituto para typeof. typeoffunciona em tipos também, enquanto decltypenão. Por exemplo, typeof(int)é intenquanto decltype(int)é um erro.
precisa

1
" type_infoobjetos que descrevem tipos diferentes devem comparar diferentes" . Na verdade, isso não é garantido . O operador de desigualdade foi removido no C ++ 20 para (presumo) desencorajar a dependência de tipos diferentes comparando não-iguais. Mas se você pensar bem, a igualdade não é segura se a desigualdade não é segura.
Indiana Kernick 27/01

50

A principal diferença entre os dois é a seguinte

  • typeof é uma construção de tempo de compilação e retorna o tipo conforme definido no tempo de compilação
  • typeid é uma construção de tempo de execução e, portanto, fornece informações sobre o tipo de tempo de execução do valor.

Tipo de referência: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

Referência typeid: https://en.wikipedia.org/wiki/Typeid


Obrigado, JaredPar! Tenho algumas perguntas novas na postagem atualizada depois de ler suas respostas. Como se também fosse verdade que seus retornos são usados ​​para propósitos diferentes: o retorno de typeof é usado como palavra-chave type que pode definir variável, mas o retorno de typeid não pode?
Tim

26

typeidpode operar em tempo de execução e retornar um objeto que descreve o tipo de tempo de execução do objeto, que deve ser um ponteiro para um objeto de uma classe com métodos virtuais para que o RTTI (informações sobre o tipo de tempo de execução) seja armazenado na classe. Também pode fornecer o tipo de tempo de compilação de uma expressão ou nome de tipo, se não for fornecido um ponteiro para uma classe com informações do tipo em tempo de execução.

typeofé uma extensão GNU e fornece o tipo de qualquer expressão em tempo de compilação. Isso pode ser útil, por exemplo, na declaração de variáveis ​​temporárias em macros que podem ser usadas em vários tipos. Em C ++, você normalmente usaria modelos .


5
Até onde eu sei, typeidaceitará qualquer expressão, não apenas aquelas que avaliam objetos com métodos virtuais. Além disso, typeidaceitará um nome de tipo , não apenas uma expressão. Você pode dizer typeid(5)ou typeid(std::string)se quiser.
Rob Kennedy

1
Esclarei minha resposta para deixar isso claro; typeid pode retornar informações do tipo em tempo de execução, se disponível, mas fornecerá informações do tipo em tempo de compilação para qualquer outra coisa.
31909 Brian Campbell

Obrigado, Brian e Rob! Tenho algumas perguntas novas na postagem atualizada depois de ler suas respostas.
Tim

22

Respondendo à pergunta adicional:

meu código de teste a seguir para typeid não gera o nome do tipo correto. o que há de errado?

Não há nada errado. O que você vê é a representação em cadeia do nome do tipo. O C ++ padrão não força os compiladores a emitirem o nome exato da classe, cabe apenas ao implementador (fornecedor do compilador) decidir o que é adequado. Em resumo, os nomes são do compilador.


Essas são duas ferramentas diferentes. typeofretorna o tipo de uma expressão, mas não é padrão. No C ++ 0x, existe algo chamado decltypeque faz o mesmo trabalho AFAIK.

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

Considerando que typeidé usado com tipos polimórficos. Por exemplo, 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.
}

Obrigado, Arak! Acabei de atualizar o post com algumas perguntas novas. Por favor, dê uma olhada, se possível.
Tim

4

typeid fornece o tipo de dados em tempo de execução, quando solicitado. Typedef é uma construção de tempo de compilação que define um novo tipo conforme declarado depois disso. Não há typeof no C ++ Output aparece como (mostrado como comentários 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

3

Você pode usar o Boost demangle para obter um nome bonito:

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

e algo como

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.