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

Почему std:: type_info полиморфна?

Есть ли причина, по которой std::type_info задан как полиморфный? Деструктор указан как виртуальный (и есть комментарий к эффекту "так, чтобы он был полиморфным" в "Проекте и эволюции С++" ). Почему я не вижу убедительной причины. У меня нет какого-либо конкретного варианта использования, мне просто интересно, есть ли когда-нибудь обоснование или история.


Вот некоторые идеи, которые я придумал и отклонил:

  • Это точка расширяемости. Реализации могут определять подклассы, а затем программы могут пытаться dynamic_cast a std::type_info на другой производный тип, определенный реализацией. Возможно, это причина, но, похоже, для реализаций так же просто добавить элемент, определенный реализацией, который может быть виртуальным. Программы, желающие протестировать эти расширения, в любом случае обязательно не переносятся.
  • Чтобы гарантировать, что производные типы будут уничтожены должным образом, когда delete с базовым указателем. Но нет стандартных производных типов, пользователи не могут определять полезные производные типы, потому что type_info не имеет стандартных публичных конструкторов, поэтому delete указатель type_info никогда не является законным и переносимым. И производные типы не полезны, потому что они не могут быть построены - единственное, что я знаю для таких неконструктивных производных типов, - это реализация таких вещей, как черта типа is_polymorphic.
  • Открывает возможность метаклассов с настраиваемыми типами - каждый реальный полиморфный class A получит производный "метакласс" A__type_info, который происходит от type_info. Возможно, такие производные классы могут выставлять членов, которые вызывают new A с различными аргументами конструктора безопасным типом, и тому подобное. Но создание самого полиморфизма type_info фактически делает такую ​​идею практически невозможной для реализации, потому что вам придется иметь метаклассы для ваших метаклассов, бесконечность, что является проблемой, если все объекты type_info имеют статическую память. Возможно, запрет на это является причиной его полиморфности.
  • В некоторых случаях использование RTTI-функций (отличных от dynamic_cast) до самого std::type_info, или кто-то думал, что это было мило или неловко, если type_info не был полиморфным. Но, учитывая, что нет стандартного производного типа и других классов стандартной иерархии, на которые можно было бы разумно попробовать перебросить, возникает вопрос: что? Существует ли использование выражений типа typeid(std::type_info) == typeid(typeid(A))?
  • Это потому, что разработчики создадут свой собственный производный тип (как я считаю, GCC делает). Но зачем это спрашивать? Даже если деструктор не был указан как виртуальный, и разработчик решил, что это должно быть, конечно, что реализация может объявить его виртуальной, поскольку она не изменяет набор разрешенных операций на type_info, поэтому переносная программа не будет быть в состоянии сказать разницу.
  • Это как-то связано с компиляторами с частично совместимыми ABI, сосуществующими, возможно, в результате динамической компоновки. Возможно, разработчики могли распознать свой собственный подкласс type_info (в отличие от одного источника от другого поставщика) портативным способом, если type_info гарантированно будет виртуальным.

Последний из них наиболее правдоподобен для меня на данный момент, но он довольно слаб.

4b9b3361

Ответ 1

Я предполагаю это там для удобства разработчиков. Это позволяет им определять расширенные классы type_info и удалять их с помощью указателей на type_info при выходе из программы, без необходимости создавать специальную магию компилятора для вызова правильного деструктора или, иначе, переходить через обручи.

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

Я не думаю, что это правда. Рассмотрим следующее:

#include <typeinfo>

struct A {
    int x;
};

struct B {
    int x;
};

int main() {
    const A *a1 = dynamic_cast<const A*>(&typeid(int));
    B b;
    const A *a2 = dynamic_cast<const A*>(&b);
}

Независимо от того, разумно это или нет, первый динамический кастинг допускается (и оценивается нулевым указателем), тогда как второй динамический кастинг не допускается. Итак, если в стандарте type_info был задан не виртуальный деструктор по умолчанию, но реализация добавила виртуальный деструктор, тогда переносимая программа могла бы определить разницу [*].

Мне кажется, проще поставить виртуальный деструктор в стандарт, чем на:

a) поместить примечание в стандарт, что, хотя определение класса подразумевает, что type_info не имеет виртуальных функций, разрешено иметь виртуальный деструктор.

b) определить набор программ, которые могут различать, является ли type_info полиморфным или нет, и запретить их все. Они не могут быть очень полезными или продуктивными программами, я не знаю, но чтобы их запретить, вы должны придумать какой-то стандартный язык, который описывает конкретное исключение, которое вы делаете для обычных правил.

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

Если бы это было запрещено, то реализация могла бы:

  • добавить виртуальный деструктор к некоторому производному классу type_info
  • выводит все его объекты типаinfo из этого класса
  • использовать это внутренне как полиморфный базовый класс для всего

который бы разрешил ситуацию, описанную выше, но статический тип выражения typeid все равно будет const std::type_info, поэтому реализациям будет сложно определить расширения, где программы могут dynamic_cast к различным целям, чтобы увидеть, какой тип объекта type_info у них есть в конкретном случае. Возможно, стандарт надеялся, что это возможно, хотя реализация всегда может предлагать вариант typeid с другим статическим типом или гарантировать, что static_cast для определенного класса расширений будет работать, а затем пусть программа dynamic_cast из есть.

В целом, насколько я знаю, виртуальный деструктор потенциально полезен для исполнителей, и удаление его не дает никому ничего, кроме того, что мы не будем тратить время на то, чтобы узнать, почему он там; -)

[*] На самом деле, я этого не продемонстрировал. Я продемонстрировал, что незаконная программа будет, при прочих равных условиях, компилироваться. Но реализация, возможно, может обойти это, гарантируя, что все не является равным, и что оно не компилируется. Boost is_polymorphic не переносится, поэтому, если программа может проверить, что класс является полиморфным, это должно быть, не может быть никакой возможности для соответствующей программы проверить, что класс не является полиморфным, t be. Я думаю, что даже если это невозможно, доказать, что для того, чтобы удалить одну строку со стандарта, требуется довольно много усилий.

Ответ 2

В стандарте С++ указано, что typeid возвращает объект типа type_info, или его подкласс, или его подкласс. Итак... Я думаю, это в значительной степени ответ. Поэтому я не понимаю, почему вы отклоняете свои баллы 1 и 2.

Пункт 5.2.8. Пункт 1 текущего стандарта С++ гласит:

Результат выражения typeid является lvalue статического типа const std:: type_info (18.5.1) и динамический type const std:: type_info или const имя, где определенный для реализации класс из std:: type_info, который сохраняет поведение, описанное в 18.5.1.61) Время жизни объекта, на которое ссылается по lvalue продолжается до конца программа. Независимо от того, destructor вызывается для type_info объект в конце программы не определено.

Что в свою очередь означает, что можно написать следующий код, является законным и точным: const type_info& x = typeid(expr);, который может потребовать, чтобы type_info был полиморфным

Ответ 3

О простейшем "глобальном" идентификаторе, который вы можете иметь в С++, является именем класса, а typeinfo предоставляет способ сравнения такого идентификатора для равенства. Но дизайн настолько неудобен и ограничен, что вам нужно обернуть typeinfo в некоторый класс-оболочку, например. чтобы иметь возможность помещать экземпляры в коллекции. Андрей Александреску сделал это в своем "Modern С++ Design", и я думаю, что эта обертка typeinfo является частью библиотеки Loki; там, вероятно, тоже в Boost; и это довольно легко сворачивать, например. см. мою собственную обертку.

Но даже для такой оболочки вообще нет необходимости в виртуальном деструкторе в typeinfo.

Поэтому возникает вопрос не столько о том, "а почему существует виртуальный деструктор", а, скорее, как я вижу, "да, почему дизайн настолько отсталый, неудобный и не поддается непосредственному использованию"? И я бы поставил это на процесс стандартизации. Например, iostreams также не являются примерами превосходного дизайна; не то, чтобы подражать.

Ответ 4

3/Открывает возможность метаклассов с настраиваемыми типами - каждый реальный полиморфный class A получит производный "метакласс" A__type_info, который происходит от type_info. Возможно, такие производные классы могут выставлять членов, которые вызывают new A с различными аргументами конструктора безопасным способом, и тому подобное. Но создание самого полиморфного type_info фактически делает такую ​​идею практически невозможной для реализации, потому что вам придется иметь метаклассы для ваших метаклассов, до бесконечности, что является проблемой, если все объекты type_info имеют статическую продолжительность хранения. Возможно, запрет на это является причиной его полиморфности.

Умный...

Во всяком случае, я не согласен с этими рассуждениями: такая реализация может легко исключить мета-классы для типов, полученных из type_info, включая type_info.