C ++中的'typeid'与'typeof'


159

我想知道C ++ typeidtypeofC ++ 之间的区别是什么。这是我所知道的:

  • typeid是在C ++头文件typeinfo中定义的type_info文档中提到的 。

  • typeof在C的GCC扩展和C ++ Boost库中定义。

另外,这是我在发现typeid未返回预期结果的地方创建的测试代码test 。为什么?

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中看到的类型名称完全相同。改名在这里可以做什么?
蒂姆(Tim)2009年

如果您像我一样不熟悉typeid:您需要基本类型中的虚函数才能打开vtable,否则最后一行将打印基本类型。
jw_

Answers:


199

C ++语言没有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也是一种选择,恕我直言。我个人有时会很困惑,因为我必须在工作中使用preC ++ 11,有时我不确定什么是真正的“ pre11”或“ post11”。
idclev 463035818

11
仅供参考,decltype不能代替typeof。不适typeof用于类型,而decltype不能。例如,typeof(int)is intwhile decltype(int)是错误。
Shahbaz

1
type_info描述不同类型的对象应比较不相等”。实际上,不能保证。不等式运算符在C ++ 20中删除,以防止(我认为)依赖于不同类型的比较不相等。但是如果您考虑一下,如果不平等不安全,平等就不安全。
印第安纳·克尼克

51

两者之间的主要区别如下

  • typeof是一个编译时构造,并返回编译时定义的类型
  • typeid是运行时构造,因此提供有关值的运行时类型的信息。

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

typeid参考:https : //en.wikipedia.org/wiki/Typeid


谢谢你,JaredPar!阅读您的回复后,我在更新后的帖子中有一些新问题。例如,也可以将它们的返回用于不同的目的:将typeof的返回用作可以定义变量的type关键字,但是typeid的返回不能吗?
蒂姆

26

typeid可以在运行时运行,并返回一个描述该对象的运行时类型的对象,该对象必须是指向具有虚拟方法的类的对象的指针,以便将RTTI(运行时类型信息)存储在该类中。如果未提供指向具有运行时类型信息的类的指针,它还可以提供表达式的编译时类型或类型名称。

typeof是GNU扩展,可在编译时为您提供任何表达式的类型。例如,这在声明可用于多种类型的宏中的临时变量时很有用。在C ++中,通常会使用模板来代替。


5
据我所知,typeid它将接受任何表达式,而不仅仅是接受使用虚拟方法对对象求值的表达式。此外,typeid将接受类型名称,而不仅仅是表达式。您可以说,typeid(5)typeid(std::string)可以。
罗伯·肯尼迪

1
我已经澄清了我的答案,使之更加清楚。typeid 可以返回运行时类型信息(如果有),但是将为其他任何内容提供编译时类型信息。
布赖恩·坎贝尔

谢谢Brian和Rob!阅读您的回复后,我在更新后的帖子中有一些新问题。
Tim的

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

谢谢你,阿拉克!我刚刚用一些新问题更新了帖子。如果可以的话请看看。
蒂姆(Tim)2009年

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.