'typeid' против 'typeof' в C ++


159

Мне интересно, в чем разница между typeidи typeofв C ++. Вот что я знаю:

  • typeidупоминается в документации для type_info, которая определена в заголовочном файле C ++ typeinfo .

  • typeofопределяется в расширении GCC для C и в библиотеке C ++ Boost .

Кроме того, вот тестовый код, который я создал, где я обнаружил, typeidкоторый не возвращает того, что я ожидал. Зачем?

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

вывод:

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

8
Как вы думаете, каким образом ваш код не печатает правильные имена типов? Это выглядит хорошо для меня. Фактическая возвращаемая строка определяется name()реализацией. Это не обязательно должно быть допустимое имя идентификатора C ++, просто то, что однозначно идентифицирует тип. Похоже, ваша реализация использует общую схему компиляции имен компилятора.
Роб Кеннеди

Спасибо, Роб! Я ожидал, что они будут такими же, как имена типов, как я видел в en.wikipedia.org/wiki/Typeid. Что тут можно делать по имени?
Тим

Если вы новичок в typeid, как я: вам нужна виртуальная функция в базовом типе, чтобы включить vtable, или последняя строка напечатает базовый тип.
jw_

Ответы:


200

Язык С ++ не имеет такого понятия, как typeof. Вы должны искать какое-то специфичное для компилятора расширение. Если вы говорите о GCC typeof, то подобная функция присутствует в C ++ 11 через ключевое слово decltype. Опять же, C ++ не имеет такого typeofключевого слова.

typeidявляется оператором языка C ++, который возвращает информацию идентификации типа во время выполнения Он в основном возвращает type_infoобъект, который равен равенству с другими type_infoобъектами.

Обратите внимание, что единственным определенным свойством возвращаемого type_infoобъекта является его сопоставимость по равенству и неравенству, т. Е. type_infoОбъекты, описывающие разные типы, должны сравниваться неравно, в то время как type_infoобъекты, описывающие один и тот же тип, должны сравниваться одинаково. Все остальное определяется реализацией. Методы, которые возвращают различные «имена», не гарантируют возвращение чего-либо понятного человеку, и даже не гарантируют, что они вообще что-либо будут возвращать.

Также обратите внимание, что приведенное выше, вероятно, подразумевает (хотя стандарт, по-видимому, не упоминает об этом явно), что последовательные применения одного typeidи того же типа могут возвращать разные type_infoобъекты (которые, конечно, все равно должны сравниваться).


1
не нужно ли это обновление, поскольку C ++ 11 имеет decltype? Я не уверен, какова общая политика, но так как вопрос помечен, C++я ожидаю, что он будет ссылаться на последний стандарт. Повторная пометка вопроса C++03также будет вариант imho. Лично меня иногда очень смущает, так как я вынужден использовать preC ++ 11 на работе, а иногда я не уверен, что такое «pre11» или «post11».
idclev 463035818

11
К вашему сведению, decltypeэто не замена typeof. typeofработает над типами, а пока decltypeнет. Например, typeof(int)это в intто время как decltype(int)ошибка.
Шахбаз

1
« type_infoобъекты, описывающие разные типы, должны сравниваться неравно» . На самом деле, это не гарантировано . Оператор неравенства был удален в C ++ 20, чтобы (я полагаю) препятствовать использованию разных типов, сравнивающих неравные. Но если подумать, равенство не безопасно, если неравенство небезопасно.
Индиана Керник

51

Основное различие между ними заключается в следующем

  • typeof является конструкцией времени компиляции и возвращает тип, определенный во время компиляции
  • typeid является конструкцией времени выполнения и, следовательно, предоставляет информацию о типе времени выполнения значения.

Тип ссылки: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

Ссылка на typeid: https://en.wikipedia.org/wiki/Typeid


Спасибо, ДжаредПар! У меня есть несколько новых вопросов в обновленном сообщении после прочтения ваших ответов. Например, если также верно, что их возвраты используются для разных целей: возвращение typeof используется как ключевое слово типа, которое может определять переменную, а возвращение typeid не может?
Тим

26

typeidможет работать во время выполнения и возвращать объект, описывающий тип времени выполнения объекта, который должен быть указателем на объект класса с виртуальными методами, чтобы RTTI (информация о типе времени выполнения) была сохранена в классе. Он также может дать тип времени компиляции выражения или имя типа, если не указан указатель на класс с информацией о типе времени выполнения.

typeofявляется расширением GNU и дает вам тип любого выражения во время компиляции. Это может быть полезно, например, при объявлении временных переменных в макросах, которые могут использоваться для нескольких типов. В C ++ вместо этого вы обычно используете шаблоны .


5
Насколько я знаю, typeidбудут приниматься любые выражения, а не только те, которые оценивают объекты с помощью виртуальных методов. Кроме того, typeidпримет имя типа , а не просто выражение. Вы можете сказать typeid(5)или, typeid(std::string)если хотите.
Роб Кеннеди

1
Я разъяснил свой ответ, чтобы прояснить это; typeid может вернуть информацию о типе времени выполнения, если она доступна, но предоставит информацию о типе времени компиляции для всего остального.
Брайан Кэмпбелл

Спасибо, Брайан и Роб! У меня есть несколько новых вопросов в обновленном сообщении после прочтения ваших ответов.
Тим

22

Отвечая на дополнительный вопрос:

мой следующий тестовый код для typeid не выводит правильное имя типа. в чем дело?

В этом нет ничего плохого. То, что вы видите, является строковым представлением имени типа. Стандартный C ++ не заставляет компиляторы выдавать точное имя класса, он просто зависит от разработчика (поставщика компилятора), чтобы решить, что подходит. Короче говоря, имена зависят от компилятора.


Это два разных инструмента. typeofвозвращает тип выражения, но оно не является стандартным. В C ++ 0x есть нечто, называемое decltypeAFAIK.

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

Принимая во внимание, typeidчто используется с полиморфными типами. Например, допустим, что catпроисходит 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.
}

Спасибо, Арак! Я только что обновил пост новыми вопросами. Пожалуйста, посмотрите, если это возможно.
Тим

4

typeid предоставляет тип данных во время выполнения по запросу. Typedef - это конструкция времени компиляции, которая определяет новый тип, как указано после этого. В C ++ нет typeof Вывод выглядит как (показанный в виде вписанных комментариев):

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

Вы можете использовать Boost demangle для создания красивого названия:

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

и что-то вроде

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.