Подтвердить что ты не робот

Идентификация класса без RTTI

Я нашел простое решение где-то в Интернете в классе идентификации без встроенного С++ RTTI.

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

Когда нам нужен идентификатор класса, мы просто используем:

Identity<OurClass>::id();

Мне интересно, есть ли какие-то столкновения? Может ли он возвращать одинаковый идентификатор для разных классов или другой идентификатор для тех же классов? Я пробовал этот код с g++ с разными значениями оптимизации, все выглядит нормально.

4b9b3361

Ответ 1

Прежде всего: существует такой интегральный тип, который специально предназначен для указания указателей:

  • intptr_t
  • и в С++ 11 uintptr_t

Во-вторых, хотя на практике на gcc они равны, размер указателя на объект и размер указателя на функцию (или указатель на член) могут быть разными. Поэтому было бы лучше использовать конкретный объект, а не сам метод (для стандартного соответствия).

В-третьих, он дает вам только тождество, в то время как RTTI намного богаче, поскольку он знает обо всех подклассах, к которым может быть применен данный объект, и даже позволяет перекрестные кавычки или касты по виртуальному наследованию.

Тем не менее, исправленная версия может быть полезна, я думаю:

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

И в иерархиях, имеющих функцию virtual, возвращающую этот идентификатор.

Для полноты я упомянул, что Clang и LLVM имеют свой собственный способ справиться с идентификацией объекта без RTTI. Вы можете прочитать об их способе реализации isa, cast и dyn_cast здесь.

Ответ 2

Эта версия позволяет избежать поведения undefined (и предупреждений компилятора):

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

Ответ 3

Это решение бросает указатель на int. Нет гарантии, что этот указатель вписывается в int, хотя на практике sizeof(void *) == sizeof(void (*)()) <= sizeof(int)

Изменить: Мне плохо. На x86_64 sizeof(int) = 4, sizeof(void (*)()) = 8, поэтому возможны столкновения и непредсказуемы.

Вы можете отличить интеграл от соответствующего размера, но все же это поведение undefined теоретически.