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

С++: type_info для различения типов

Я знаю, что компиляторы имеют большую свободу в реализации поведения функций std::type_info.

Я собираюсь использовать его для сравнения типов объектов, поэтому я хотел бы быть уверен, что:

  • std::type_info::name должен возвращать две разные строки для двух разных типов.

  • std::type_info::before должен сказать, что Type1 до Type2 исключение - или Type1 перед Type2.

    // like this:
    typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
    
  • Две разные специализации одного и того же класса шаблонов считаются разными типами.

  • Два разных типа typedef одного типа - это один и тот же тип.

И наконец:

  • Так как std::type_info не копируется, как я могу хранить type_info где-нибудь (например: в std::map)? Единственный способ иметь std::type_info всегда где-нибудь (например: в стеке или в статической/глобальной переменной) и использовать указатель на него?

  • Насколько быстрые operator==, operator!= и before для большинства распространенных компиляторов? Думаю, они должны сравнивать только значение. И как быстро typeid?

  • У меня есть класс A с virtual bool operator==( const A& ) const. Поскольку A имеет много подклассов (некоторые из которых неизвестны во время компиляции), я бы перегрузил этот виртуальный оператор в любом подклассе B следующим образом:

    virtual bool operator==( const A &other ) const {
      if( typeid(*this) != typeid(other) ) return false;
      // bool B::operator==( const B &other ) const // is defined for any class B
      return operator==( static_cast<B&>( other ) );
    }
    

    Является ли это приемлемым (и стандартным) способом реализации такого оператора?

4b9b3361

Ответ 1

После быстрого просмотра документации я бы сказал, что:

  • std:: type_info:: name всегда возвращает две разные строки для двух разных типов, иначе это означает, что компилятор потерял себя при разрешении типов, и вы больше не должны его использовать.

  • Ссылка указывает: "перед возвратом true, если тип предшествует типу rhs в порядке сортировки. Порядок сортировки - это всего лишь внутренний порядок, хранящийся в конкретной реализации, и не обязательно связан с отношениями наследования или объявлением порядка". Поэтому у вас есть гарантия того, что ни один тип не имеет того же ранга в порядке сортировки.

  • Каждый экземпляр класса шаблона является другим типом. Специализация не исключает.

  • Я не совсем понимаю, что вы имеете в виду. Если вы имеете в виду что-то вроде typedef foo bar; в двух отдельных единицах компиляции, и этот бар одинаковый для обоих, он работает именно так. Если вы имеете в виду typedef foo bar; typedef int bar;, это не сработает (кроме if foo is int).

О других вопросах:

  • Вы должны хранить ссылки на std:: type_info, обернуть его каким-то образом.
  • Абсолютно не знаю о производительности, я полагаю, что операторы сравнения имеют постоянное время, несмотря на сложность типа. Прежде чем иметь линейную сложность в зависимости от количества различных типов, используемых в вашем коде.
  • Это действительно странно imho. Вы должны перегрузить operator== вместо того, чтобы сделать его виртуальным и переопределить.

Ответ 2

Стандарт 18.5.1 (Class type_info):

Класс type_info описывает тип информации, созданной реализация. Объекты этого класса эффективно хранить указатель на имя для типа и закодированное значение подходит для сравнения двух типов для равенства или порядка сортировки. The имена, правило кодирования и сопоставление последовательность для типов не определена и могут отличаться между программами.

Из моего понимания:

  • У вас нет этой гарантии относительно std:type_info::name. Стандарт только утверждает, что name возвращает NTBS, определенный реализацией, и я считаю, что соответствующая реализация может очень хорошо возвращать одну и ту же строку для каждого типа.
  • Я не знаю, и стандарт не ясен по этому вопросу, поэтому я не буду полагаться на такое поведение.
  • Для меня должно быть определенное "Да" для меня.
  • Для меня должно быть определенное "Да" для меня.

Относительно второго набора вопросов:

  • Нет, вы не можете сохранить type_info. Андрей Александреску предлагает обертку TypeInfo в своей Modern С++ Design книге. Обратите внимание, что объекты, возвращаемые typeid, имеют статическое хранилище, поэтому вы можете безопасно хранить указатели, не беспокоясь о жизни объекта
  • Я считаю, вы можете предположить, что сравнение type_info чрезвычайно эффективно (сравнивать не так уж и много).

Ответ 3

Вы можете сохранить его так.

class my_type_info
{
public:
     my_type_info(const std::type_info& info) : info_(&info){}
     std::type_info get() const { return *info_;}
private:
     const std::type_info* info_;
};

EDIT:

Стандарт С++ 5.2.8.

Результат выражение typeid является значением l static type const std:: type_info...

Это означает, что вы можете использовать его так.

my_type_info(typeid(my_type));

Функция typeid возвращает значение lvalue (оно не является временным), поэтому адрес возвращаемого типа type_info всегда действителен.

Ответ 4

Текущие ответы на вопросы 1 и 2 совершенно правильны, и они по сути являются просто деталями для класса type_info - нечего повторять эти ответы.

Для вопросов 3 и 4 важно понять, что именно представляет собой тип на С++, и как они относятся к именам. Во-первых, существует целая куча предопределенных типов, а имена имеют имена: int, float, double. Далее, есть некоторые построенные типы, которые не имеют собственных имен: const int, int*, const int*, int* const. Существуют типы функций int (int) и типы указателей функций int (*)(int).

Иногда бывает полезно указать имя неназванного типа, что возможно с помощью typedef. Например, typedef int* pint или typedef int (*pf)(int);. Это вводит имя, а не новый тип.

Далее следуют пользовательские типы: структуры, классы, союзы. Там хорошее соглашение, чтобы дать им имена, но это не обязательно. Не добавляйте такое имя с помощью typedef, вы можете сделать это напрямую: struct Foo { }; вместо typedef struct {} Foo;. Обычно в заголовках есть определения классов, которые заканчиваются\в нескольких единицах перевода. Это означает, что класс определен более одного раза. Это по-прежнему один и тот же тип, и поэтому вам не разрешено играть в трюки с помощью макросов, чтобы изменить определения членов класса.

Класс шаблона не является типом, это рецепт для типов. Два экземпляра одного шаблона класса являются различными типами, если аргументы шаблона являются разными типами (или значениями). Это работает рекурсивно: учитывая template <typename T> struct Foo{};, Foo<Foo<int> > является тем же типом, что и Foo<Foo<Bar> >, если и только если Bar - это другое имя для типа int.

Ответ 5

Type_info - это реализация, поэтому я действительно не буду полагаться на нее. Однако, основываясь на моих опытах с использованием g++ и MSVC, предположения 1,3 и 4 не... действительно не уверены в # 2.

Есть ли причина, по которой вы не можете использовать другой метод?

template<typename T, typename U>
struct is_same       { static bool const result = false; };

template<typename T>
struct is_same<T, T> { static bool const result = true;  };

template<typename S, typename T>
bool IsSame(const S& s, const T& t) {   return is_same<S,T>::result; }