为啥非多态 typeid 需要 RTTI?
Posted
技术标签:
【中文标题】为啥非多态 typeid 需要 RTTI?【英文标题】:Why is RTTI needed for non-polymorphic typeid?为什么非多态 typeid 需要 RTTI? 【发布时间】:2020-07-28 11:06:14 【问题描述】:我有以下代码:
template<typename T>
class genericHandlerpublic: 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
在回答下的评论中,您写了一些关于依赖周期的内容。请描述这个问题,因为显然这是您问题的实际根源,并且有很多技巧可以打破这种循环。
【参考方案1】:
你问了两个问题。
RTTI是运行时类型信息,在非多态typeid的情况下应该不需要...
但这是编译器开关,不是语言功能,所以你应该检查compiler documentation:
-fno-rtti
禁止生成有关每个具有虚函数的类的信息,以供 C++ 运行时类型识别功能(
dynamic_cast
和typeid
)使用。 如果您不使用该语言的这些部分,则可以使用此标志节省一些空间。请注意,异常处理使用相同的信息,但它会根据需要生成它。dynamic_cast
运算符仍可用于不需要运行时类型信息的强制转换,即强制转换为 void * 或明确的基类。
(我的重点)。
所以开关禁用整个 typeinfo 系统以节省空间。如果您想要typeinfo
(或者您想要使用使用typeinfo
的标准库工具),则不要使用编译器选项显式禁用typeinfo
。
...可以在编译时推导出的非多态类型标识
编辑,正如 Wakely 先生指出的那样,问题在于您在自己的代码中明确使用了 typeid
。
虽然std::any
可能能够在不使用 RTTI 的情况下从***对象中擦除存储的类型,但这取决于实现,并且它肯定如果不使用typeid
/typeinfo
你告诉 GCC 不要生成。
哦,我忘了
我错过了什么吗?
是的,问你的实际问题,即
我删除它是因为我需要解决循环模板依赖性,遗憾的是我不能在这里“简单地不删除它”。 我是否有任何不使用 RTTI 的替代方案
当然,类型擦除不依赖于 RTTI。
只有 自动 类型的擦除取决于 RTTI,如上所述,并非全部。您可以避免 std::any::type()
或手动编写自己的可区分联合 - 您需要枚举类型,但只有枚举本身需要对 DU 的所有用户可见。
【讨论】:
我正在删除它,因为我需要解决循环模板依赖性,遗憾的是我不能在这里“简单地不删除它”。我有任何不使用 RTTI 的替代方案吗? @WernerHenze 但它也说“如果你不使用语言的那些部分” - 意思是dynamic_cast
除非特别允许和 typeid
- 然后你可以使用 -fno-rtti旗帜。暗示如果根本需要typeid
,它就不能使用。
@Magix std::any
与 typeid
和 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 genericHandlerpublic: 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
【讨论】:
以上是关于为啥非多态 typeid 需要 RTTI?的主要内容,如果未能解决你的问题,请参考以下文章