【问题标题】:Is typeid of type name always evaluated at compile time in c++?类型名称的 typeid 是否总是在 C++ 中的编译时评估?
【发布时间】:2010-05-20 19:41:29
【问题描述】:

我想检查 typeid 在与类型名称(即 typeid(int)、typeid(std::string)...)一起使用时在编译时进行评估。

为此,我在一个循环中重复了两个 typeid 调用的比较,并在启用优化的情况下对其进行编译,以查看编译器是否简化了循环(通过查看执行时间是 1us,而当它简化时160 毫秒,而不是)。

我得到了奇怪的结果,因为有时编译器简化了代码,有时却没有。我使用g++(我尝试了不同的4.x版本),程序如下:

#include <iostream>
#include <typeinfo>
#include <time.h>

class DisplayData {};

class RobotDisplay: public DisplayData {};
class SensorDisplay: public DisplayData {};

class RobotQt {};
class SensorQt {};

timespec tp1, tp2;
const int n = 1000000000;

int main()
{
    int avg = 0;
    clock_gettime(CLOCK_REALTIME, &tp1);
    for(int i = 0; i < n; ++i)
    {
//      if (typeid(RobotQt) == typeid(RobotDisplay))    // (1) compile time
//      if (typeid(SensorQt) == typeid(SensorDisplay))  // (2) compile time
        if (typeid(RobotQt) == typeid(RobotDisplay) || 
            typeid(SensorQt) == typeid(SensorDisplay))    // (3) not compile time ???!!!
            avg++;
        else
            avg--;
    }
    clock_gettime(CLOCK_REALTIME, &tp2);
    std::cout << "time (" << avg << "): " << 
        (tp2.tv_sec-tp1.tv_sec)*1000000000+(tp2.tv_nsec-tp1.tv_nsec) << 
        " ns" << std::endl;
}

这个问题出现的条件还不清楚,但是:
- 如果不涉及继承,没问题(总是编译时)
- 如果我只做一个比较,没问题
- 如果所有项都是错误的,问题只会在比较分离时出现

那么,关于 typeid 的工作原理,有什么我没有得到的(当与类型名称一起使用时,它是否总是应该在编译时进行评估?)或者这可能是评估或优化中的 gcc 错误?

关于上下文,我将问题追溯到这个非常简化的示例,但我的目标是将 typeid 与模板类型一起使用(因为无法实现部分函数模板特化)。

感谢您的帮助!

【问题讨论】:

  • 您的结论是完全基于您的代码执行需要多长时间,还是您对编译器实际输出的内容有更明确的证据?
  • 你能设计你的程序而不需要typeid吗?比较对象类型的程序被认为是格式不正确的 OO 程序。
  • Dennis> 是的,但我刚刚检查了 asm 代码,我可以确认在一种情况下,有 6 条指令在两个 clock_gettime 调用之间没有跳转,而在另一种情况下,有 15 条指令有 2 次跳转包括一个明显的循环。
  • 您已经验证编译器没有执行您认为明显的优化。但这并没有说明编译器是否在编译时执行 typeid 表达式。
  • Thomas> 我知道我应该做模板特化而不是类型比较。但在我的情况下,这会导致大量代码重复和/或增加复杂性。如果某些类还没有实现,我只想禁用代码的某些部分,类型比较确实是更具可读性和简单的解决方案,只要在编译时完成,我认为它不会那么讨厌...

标签: c++ gcc g++ compile-time typeid


【解决方案1】:

我真的不知道你的问题的答案,但如果你使用 is_same 元函数而不是 typeid 你可能会得到更理想的结果。即使你没有访问这个元函数,写一个也很容易:


template < typename T1, typename T2 >
struct is_same
{
  enum { value = false }; // is_same represents a bool.
  typedef is_same<T1,T2> type; // to qualify as a metafunction.
};

template < typename T >
struct is_same
{
  enum { value = true };
  typedef is_same<T,T> type;
};

【讨论】:

  • 太棒了!但是我不得不稍微修改一下函数才能工作: [ template struct is_same ] [ if (is_same::value) {} ] 还有一个提升::是一样的。非常感谢,我会这样做的!
【解决方案2】:

typeid 是运行时类型识别机制的一部分,这表明了它的用途:它的主要用途是在运行时识别对基类的指针/引用的动态类型。当类型在编译时静态已知时,您无需“识别”它们,因为您已经知道它们是什么。

在示例中,虽然在运行时没有什么可识别的,但结果在编译时没有任何用处(typeid 不能出现在 const 表达式中,这是模板元编程所需要的) .

所以我也推荐is_same

【讨论】:

  • 是的,我同意,我不知道 is_same,但这确实是我想要的。谢谢!
【解决方案3】:

对于任何类型 T,如果 T 是多态的,编译器需要在运行时评估 typeid 的东西。如果 T 是非多态的,编译器需要在编译时评估 typeid 的东西。但是,我在 C++ 草案 (n3000.pdf) 中找不到相关参考。

事实上,在我参与的一个项目中,这个技巧被用来发现一个类在运行时是否是多态的

template <class T>  
bool isPolymorphic() {  
    bool answer=false; 
    T *t = new T(); 
    typeid(answer=true,*t);  
    delete t; 
    return answer;  
} 

几个月前我在 SO 上问过一个相关问题 here

【讨论】:

  • 好的,但是您在对象上使用 typeid,而不是类型名称。即使类型是多态的,给定类型名称它在编译时是完全已知的,不是吗?
  • new/delete 而不是仅仅声明一个本地对象有什么意义?
  • @AndreyT:任何产生lvalue 的东西都适用于 typeid 运算符。为了完整起见,我只是从另一个线程中得到答案。
  • @cyril42e:我试图回答您关于何时评估 typeid 运算符的问题。我给出的示例只是使用评估语义来做一些有用的事情。
  • 事实上不真实,但很接近。必须计算表达式并且必须返回正确的 type_info。但在上面的代码中,编译器实际上是被允许省去整个运行时的 typeid 检查的。只要answer=true 部分仍然存在,可观察到的行为就不会改变。
猜你喜欢
  • 2013-09-17
  • 2021-02-13
  • 2011-02-06
  • 1970-01-01
  • 2014-12-21
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2015-04-23
相关资源
最近更新 更多