什么时候使用“typeid”是最好的解决方案?
Posted
技术标签:
【中文标题】什么时候使用“typeid”是最好的解决方案?【英文标题】:When is using 'typeid' the best solution? 【发布时间】:2011-10-08 17:26:59 【问题描述】:不使用typeid
的原因有很多。除了使用 type_info
的成员(实现定义的行为)之外,通常(总是?)可以使用其他 C++ 语言特性来提供类似的功能,例如:重载、虚函数等。
那么,除了依赖于实现定义的行为的使用之外,有没有人有一个真实的例子,typeid
是最好的解决方案?
【问题讨论】:
好问题;没有我能想到的:-) 作为一个非 C++ 开发人员,我一直很想知道为什么它会是实现定义的行为,而不是仅仅返回完全限定的类名... @Christopher:对于模板,完全限定的类名可能会变得相当复杂。在我当前的应用程序中,我有大约 20 种类型的模板向量,这是一个相当大的名字(发生错误时,我的控制台上有几行)。规范为实现提供了一些回旋余地来防止这种爆炸。 【参考方案1】:boost::any
使用typeid
来实现any_cast
。
template<typename T> any_cast(const any& other)
if(typeid(T) != other.type()) throw bad_any_cast();
//...actual cast here...
你不能确定T
是多态的,所以dynamic_cast
是不可能的,并且boost::any
调用中的封闭类型现在已经丢失了,所以其他类型的转换都不能提供任何类型类型安全。
【讨论】:
我认为这是最接近真实答案的。这是最接近真实代码情况的情况,typeid
比替代方案更好。虽然,这是基于它抛出异常而不是说“断言”——我想知道这是否仅仅是因为 boost 更喜欢因异常而不是断言而失败。【参考方案2】:
当实现多方法(或多调度)时,实际调用是从例如一张地图,使用std::type_info*
作为键。
【讨论】:
该死,我知道我忘记了一个例子。我昨天真的想过这个,但认为它不需要typeid
,虽然很明显它确实需要。
还有其他方法可以实现基于类类型的键吗?只是好奇?
@Christopher 另一种选择是“手动”RTTI,其中涉及的类型有例如一个get_type_token
函数成员来识别它们(脆弱且强耦合的解决方案)。这也需要对基本类型进行装箱,或者有一个令牌以其他方式传递它们。【参考方案3】:
写一个动态树,你可以在运行时修改树的结构,每个链接都有不同的类型,它需要typeid。 dynamic_cast 还不够。
编辑:这里有一些细节:
class I
public:
virtual std::string type() const=0;
virtual void *value() const=0;
;
template<class T>
class Impl : public I
public:
Impl(T t) : t(t)
std::string type() const return typeid(T).name();
void *value() const return &t;
private:
T t;
;
然后用这些构建一棵树:
template<class Node, class Link>
class Tree ;
链接类型为 I* 接口...由于上述适用于 T1、T2、T3、T4 类型的任何值,我们也可以对任何函数 T->T1、T->T2 使用类似的类, T->T3, T->T4,并使用该函数类型作为树的节点。现在您已经在动态树中描述了正确的表达式。
【讨论】:
好的...所以你至少可以得到+1,因为它听起来好像真的很有用! ;) 你能充实一下吗? 不相信。我需要更多细节,但我敢打赌我可以通过界面实现同样的效果。 @tp1:请检查我是否理解正确:这种设计可用于 T1、T2 等与继承无关且我们需要动态构建树的情况。为了从树中的一个节点到类型,我们需要从 'std::string::type()' 的结果再次映射到真实类型? @richard corden:嗯,该类型只在派生类中使用。任何浏览树的东西都只能使用基类。因此,基类需要浏览树的算法所需的所有成员函数。他们只是不能直接将类型用于任何事情。他们可能会将 void* 传递给树中的另一个节点,但需要首先检查类型是否相同。通常使用它是为了让您拥有接受不同类型的不同操作或函数的集合,并过滤掉那些类型错误的函数。 @tp1: 嗯....所以我想知道'typeid'给了我们什么?如果我理解您的最后一条评论,您的意思是class I
无论如何都实现了所需的接口 - 在这种情况下,我们可以只使用访问者模式,我们会编写访问者,它将在他们关心的节点上运行并忽略他们的节点别。不需要typeid
或void*
。【参考方案4】:
我用它来探测我的全部捕获处理程序中异常的类类型。
// fudge vtable existence (thrown exceptions must have one)
class StubException
virtual ~StubException();
;
.....
catch(...)
StubException *e = getExceptionObject(); // compiler/rt specific
std::string s = typeid(e).name();
...
throw;
函数getExceptionObject()
是一个小型实用程序库的一部分,我错误地访问了有关异常的其他信息。当一个函数抛出一个我应该被捕获但没有被捕获的异常时,它会派上用场。多年来,自从我立即知道需要覆盖的异常类型以来,它无疑节省了很多挫败感。
【讨论】:
【参考方案5】:您可以使用typeid
来比较两个对象的实际类型。如果您想检查两个对象的相等性,并且首先确保它们是完全相同的类型,这可能很有用(尽管我必须说我没有看到很多这样做,所以可能有一个很好的理由为什么这不是好主意...)。
【讨论】:
完全正确:你能举出一个 single 用例,以及使用dynamic_cast
无法更好/同样好解决的用例吗?
@Konrad Rudolph:是的,有。 dynamic_cast 将派生类转换为基类就好了,所以如果被调用的比较运算符在基类中,您将无法获得所需的功能。
@eran:你能提供一个真实世界的例子吗?根据我目前的经验,我会说需要这种“比较”级别的设计并不可靠。听说我开悟了!
@Konrad, Richard - 我想不出一个真实的例子,但由于这种检查在 Java 中很常见,我认为它在 C++ 中的某些情况下也可能有用。我认为 Java 和 C++ 之间的这种差异超出了这个问题的范围,所以I've asked another one。【参考方案6】:
那么,除了依赖于实现定义的行为的使用之外,有没有人有一个真实世界的例子,其中 typeid 是最好的解决方案?
我有时会在调试输出中使用它,以验证传递给我的函数的模板参数确实属于给定类型。这对我来说是有意义的,因为传递给我的函数的实际模板参数是由一个专门的元函数生成的,我想确保使用正确的元函数。
【讨论】:
+1 是一个相当不错的用例,但我不确定这是否是 typeid 的杀手锏。 @Konrad:我结合使用typeid
和 demanling 进行日志记录。很有帮助。
@MatthieuM。我也在一个项目中使用过它,但在某些时候,为每个类重载或覆盖某些 class_name
方法变得更容易。
@Konrad:我实际上用它来生成类名,但我没有很深的层次结构,通常基类子类是final,所以我只需要在base 和一个孩子“神奇地”生成了这个名字。
是的,我曾经使用 typeid 作为 std::logic_error 派生异常的一部分进行测试。我有一个模板代码,其中包含一个执行一些特殊边界检查逻辑的容器,如果我试图将容器置于无效状态,我会抛出这个异常。我使用 typeid 让我快速查看类名是什么。我没有做任何特殊的解构,所以我看到了很多类似GGobLedeGooKKMyClassNameOoGaBooGGaA
的东西,但它只是为了调试东西所以我并不在意。以上是关于什么时候使用“typeid”是最好的解决方案?的主要内容,如果未能解决你的问题,请参考以下文章
由于 typeid().raw_name(),c++ 代码无法使用 GCC 编译 - 我该如何解决这个问题?
织梦dede:channel指定typeid子栏目调用currentstyle高亮无效的解决教程
Asp.net 中List集合的select方法怎么使用,最好能给个小例子
MyBatis使用MyBatis的分页组件PageHelper时,多表关联下使用别名查询时,前台传参过来,根据参数排序的解决方案