2010-10-08 4 views
16

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


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

  1. Это точка расширяемость - реализации могут определять подклассы, а также программы, то можно попытаться dynamic_caststd::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 гарантированно был виртуальным.

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

ответ

9

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

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

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

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

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

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

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

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

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

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

9

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

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

Результат выражения TypeID является именующим статическим типа Const станда :: type_info (18.5.1) и динамического типа Const Std :: type_info или Const имени, где имя является определяется реализация класса, производный от станда :: type_info, который сохраняет поведения, описанное в 18.5. 1.61) Срок службы объекта, указанного в по lvalue продолжается до конца программы. Вызывается ли деструктор для объекта type_info в конце программы: не указано.

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

+1

Да, я понимаю, но я не вижу ничего портативного, что вы можете сделать с информацией о том, что это подкласс, или нет. Похоже, что все портативные, действующие программы теперь будут действительны, если они не будут полиморфными. Делать это так, чтобы вы не могли обнаруживать подклассы (делая type_info не полиморфными) не должны ничего менять для переносных программ, насколько я могу судить, и не должны создавать непереносимые расширения, которые сложнее создать. Можете ли вы придумать контрпример, что-то, что стало возможным благодаря этой спецификации? – Doug

+0

@Doug: см. Мое редактирование –

+1

Спасибо, но я до сих пор не понимаю, почему это последнее выражение требует, чтобы 'type_info' был полиморфным. Все полезные функции на 'type_info', такие как' operator == ',' before', 'name' и т. Д., Не указаны как виртуальные. Поэтому, если для реализации требуется виртуальная диспетчеризация этих функций, я предполагаю, что она может объявить их виртуальными или делегировать функции виртуальной реализации, а если нет, то нет необходимости делать их виртуальными. Есть что-то, что мне не хватает? – Doug

2

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

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

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

+1

У меня вроде бы есть ответ на вопрос: «Да, почему дизайн настолько отсталый, неудобный и не поддается непосредственному использованию?». Это в основном потому, что Бьярне Страуструп (а) хотел не дать людям слишком полагаться на RTTI, и (б) получил множество запросов на все виды связанных с RTTI функций, некоторые из которых нарушили безопасность типа C++. Он считал, что было бы очень сложно удовлетворить все просьбы, а также быть потенциально высокооплачиваемыми и технически сомнительными, поэтому проще всего обеспечить простоту, которую люди могли бы использовать в соответствии с их потребностями. – Doug

2

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

Clever ...

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

Смежные вопросы