Почему std :: type_info полиморфен? - PullRequest
16 голосов
/ 08 октября 2010

Есть ли причина, по которой std::type_info считается полиморфным?Деструктор определен как виртуальный (и есть комментарий к эффекту «чтобы он был полиморфным» в The Design and Evolution of C ++).Я действительно не вижу убедительной причины, почему.У меня нет конкретного варианта использования, мне просто интересно, было ли когда-нибудь обоснование или история за этим.


Вот некоторые идеи, которые я выдвинул и отклонил:

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

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

Ответы [ 4 ]

9 голосов
/ 08 октября 2010

Полагаю, это для удобства разработчиков.Это позволяет им определять расширенные 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 не имеет виртуальных функций, разрешено иметь виртуальныйдеструктор.

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

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

Если бы его запретили,хотя, тогда реализация могла бы:

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

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

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

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

9 голосов
/ 08 октября 2010

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

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

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

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

2 голосов
/ 23 ноября 2011

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

Умный ...

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

2 голосов
/ 08 октября 2010

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

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

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

...