没有运行时类型信息的类身份识别

13

我在互联网上找到了一个简单的解决方案,用于创建一个没有内置C++ RTTI 的身份类。

template <typename T>
class Identity {
public:
    static int64_t id()
    {
        static int64_t dummy;
        return reinterpret_cast<int64_t>(&dummy);
    }
};

当我们需要一些类的ID时,我们只需使用:

Identity<OurClass>::id();
我在想,这种情况是否会发生碰撞?它能否返回不同类别的相同ID,或者相同类别的不同ID?我已经尝试在g++中使用不同的优化值运行了此代码,一切似乎都正常。

原则上是可以的。但不能保证函数指针和“int”类型的大小相同。 - Oliver Charlesworth
1
这与我的兴趣相关... - Tom Knapen
为了避免上述问题,最好在静态成员函数模板中放置一个静态的int变量,并返回指向它的指针。编译器将会优化该函数。 - Electro
好的,如果我们假设指针可以处理为整数?我会编辑这段代码... - pproger
@pproger:嗯,这样稍微好一些了,但不是良好定义的C++。在你的模板中只需要一个静态的int即可。 - Electro
显示剩余7条评论
3个回答

12

首先:有一种专门用于包含指针的整数类型:

  • intptr_t
  • 在 C++11 中还有 uintptr_t

其次,尽管在gcc中它们实际上相等,但对象指针和函数指针(或成员指针)的大小可能是不同的。因此,最好使用特定的对象而不是方法本身(以符合标准)。

第三,它只给你身份识别,而RTTI则更具丰富性,因为它了解给定对象可以强制转换为的所有子类,甚至允许跨虚继承的强制转换或跨越多层继承链的强制转换。

尽管如此,我认为已经纠正过的版本仍然可能是有用的:

struct Foo {
    static intptr_t Id() {
        static boost::none_t const Dummy = {};
        return reinterpret_cast<intptr_t>(&Dummy);
    }
};

在继承结构中,可以使用一个返回该对象ID的虚函数。

为了完整起见,我提到Clang和LLVM有他们自己的处理对象识别的方式而无需RTTI。您可能希望阅读关于它们实现isacastdyn_cast的方法这里


谢谢,但我不明白,为什么对象指针的大小和函数指针的大小可能不同?你能举个例子吗? - pproger
@pproger:虚成员函数指针的大小通常与普通指针不同。然而,这取决于编译器,您可以在这篇优秀的codeproject文章中了解更多信息:http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible - Tommy Andersen
@TommyA 我们这里没有任何虚函数。我们是从静态函数中获取指针的。 - pproger
@pproger:标准为编译器留下了余地。在使用VC++时,成员函数指针可能会比常规指针大4倍,而在gcc上它们的大小始终相同,因为gcc创建了一个“跳板”,即一个小型(未命名)函数,在调用您实现的真实函数之前执行必要的调整。这只是在考虑可移植性时需要记住的一些事情。 - Matthieu M.

0

这个解决方案将函数指针转换为一个 int。虽然在实践中 sizeof(void *) == sizeof(void (*)()) <= sizeof(int),但不能保证该指针适合于一个 int

编辑:我的错误。在 x86_64 上,sizeof(int) = 4sizeof(void (*)()) = 8,因此可能会发生冲突并且是不可预测的。

您可以将其转换为适当大小的整数,但从理论上讲仍然是未定义行为。


在实践中并不总是如此;请在x86-64上尝试。 - Oliver Charlesworth

0

这个版本避免了未定义的行为(和编译器警告):

template <typename T>
class Identity {
public:
    static const int* id() { static const int id = 0; return &id; }
};

谁知道呢。你可能会问,为什么他不使用本地RTTI,结果相同) - pproger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接