【问题标题】:Why is RTTI needed for non-polymorphic typeid?为什么非多态 typeid 需要 RTTI?
【发布时间】:2020-07-28 11:06:14
【问题描述】:

我有以下代码:

template<typename T>
class genericHandler{public: using evt_t = T;};

template<typename T>
class specialHandler : public genericHandler<T> {  /* more stuff */ };

int main(int argc, char *argv[]) {
    
    std::any any_var = specialHandler<int>{};
    
    auto f = [&any_var](auto evtHandler) {
        using EventType = typename std::remove_reference<decltype(evtHandler)>::type ::evt_t;
        if(any_var.type() == typeid(EventType)) { std::cout << "yes" << std::endl; } else { std::cout << "no" << std::endl; }
    };

    auto h = specialHandler<int>{ };
    f(h);
}

Try it on Coliru

当被调用时,evtHandler 是非多态派生类型specialHandler。根据cppreference,我们有:

当应用于多态类型的表达式时,计算 typeid 表达式可能涉及运行时开销(虚拟表 查找),否则在编译时解析 typeid 表达式。

当我使用 gcc 和 -fno-rtti 编译时,我收到以下错误消息:

不能将“typeid”与“-fno-rtti”一起使用

RTTI 是运行时类型信息,在编译时可以推导出的非多态类型id 的情况下不需要。我错过了什么吗?

【问题讨论】:

  • 你的类型是非多态的,但不幸的是你不知道std::any的内部结构。
  • 比较open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1105r1.html。我可以确认它适用于 VS2017(并且禁用 rtti),但在 gcc 主干和 clang 主干上失败。
  • GCC 的 std::any 在没有 RTTI 的情况下也能正常工作,只需使用 any_cast 检查它是否拥有该类型,而不是使用 typeid,例如coliru.stacked-crooked.com/a/f68faa89814ea711
  • 在回答下的评论中,您写了一些关于依赖周期的内容。请描述这个问题,因为显然这是您问题的实际根源,并且有很多技巧可以打破这种循环。

标签: c++ gcc c++17 rtti


【解决方案1】:

你问了两个问题。

  1. RTTI是运行时类型信息,在非多态typeid的情况下应该不需要...

    但这是编译器开关,不是语言功能,所以你应该检查compiler documentation

    -fno-rtti

    禁止生成有关每个具有虚函数的类的信息,以供 C++ 运行时类型识别功能(dynamic_casttypeid)使用。 如果您不使用该语言的这些部分,则可以使用此标志节省一些空间。请注意,异常处理使用相同的信息,但它会根据需要生成它。 dynamic_cast 运算符仍可用于不需要运行时类型信息的强制转换,即强制转换为 void * 或明确的基类。

    (我的重点)。

    所以开关禁用整个 typeinfo 系统以节省空间。如果您想要typeinfo(或者您想要使用使用typeinfo 的标准库工具),则不要使用编译器选项显式禁用typeinfo

  2. ...可以在编译时推导出的非多态类型标识

    编辑,正如 Wakely 先生指出的那样,问题在于您在自己的代码中明确使用了 typeid

    虽然std::any 可能能够在不使用 RTTI 的情况下从顶级对象中擦除存储的类型,但这取决于实现,并且它肯定如果不使用typeid/typeinfo 你告诉 GCC 不要生成。

哦,我忘了

  1. 我错过了什么吗?

    是的,问你的实际问题,即

    我删除它是因为我需要解决循环模板依赖性,遗憾的是我不能在这里“简单地不删除它”。 我是否有任何不使用 RTTI 的替代方案

    当然,类型擦除不依赖于 RTTI。

    只有 自动 类型的擦除取决于 RTTI,如上所述,并非全部。您可以避免 std::any::type() 或手动编写自己的可区分联合 - 您需要枚举类型,但只有枚举本身需要对 DU 的所有用户可见。

【讨论】:

  • 我正在删除它,因为我需要解决循环模板依赖性,遗憾的是我不能在这里“简单地不删除它”。我有任何不使用 RTTI 的替代方案吗?
  • @WernerHenze 但它也说“如果你不使用语言的那些部分” - 意思是 dynamic_cast 除非特别允许和 typeid - 然后你可以使用 -fno-rtti旗帜。暗示如果根本需要typeid,它就不能使用。
  • @Magix std::anytypeid 和 RTTI 相当相关。我建议提出另一个关于潜在替代方案的问题,并提供更多背景说明为什么 std::any 似乎是合适或必要的。
  • 答案明确回答了为什么typeid 不起作用。这是因为 GCC 标志禁用了整个 typeinfo 系统。我真的不确定我能清楚多少。如果您不喜欢 GCC 文档本身,请接受他们。
  • @aschepler std::any 根本与 RTTI 无关,只有 std::any::type 必须 使用它。正如我在上面评论的那样,GCC 的 std::any 在没有 RTTI 的情况下也可以正常工作,例如coliru.stacked-crooked.com/a/f68faa89814ea711
【解决方案2】:

可以更改您的代码而不使用std::any::type()。在这种情况下,一切都按预期工作(在您的代码中修复了一些其他问题之后):

#include <iostream>
#include <any>

template<typename T>
class genericHandler{public: using evt_t = T;};

template<typename T>
class specialHandler : public genericHandler<T> {  /* more stuff */ };

int main(int argc, char *argv[]) {
    
    std::any any_var = specialHandler<int>{};
    
    auto f = [&any_var](auto evtHandler) {
        using EventType = typename std::remove_reference<decltype(evtHandler)>::type ::evt_t;
        std::cout << (std::any_cast<specialHandler<EventType>>(&any_var) != nullptr ? "yes" : "no") << std::endl;
    };

    auto h = specialHandler<int>{ };
    f(h);
    f(specialHandler<double>{});
}

在禁用 RTTI 时工作:
http://coliru.stacked-crooked.com/a/20855b15701605f1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-22
    • 2016-02-15
    • 2016-06-16
    • 2011-01-14
    • 2012-09-07
    • 1970-01-01
    • 2012-09-08
    • 1970-01-01
    相关资源
    最近更新 更多