سؤال

هل هناك سبب لماذا std::type_info تم تحديده ليكون متعدد الأشكال؟ تم تحديد المدمر ليكون افتراضيًا (وهناك تعليق على تأثير "بحيث يكون متعدد الأشكال" في تصميم وتطور C ++). لا أستطيع حقًا رؤية سبب مقنع. ليس لدي أي حالة استخدام محددة ، كنت أتساءل فقط عما إذا كان هناك من أي وقت مضى قصة منطقية أو وراءها.


إليكم بعض الأفكار التي توصلت إليها ورفضتها:

  1. إنها نقطة تمديد - قد تحدد التطبيقات الفئات الفرعية ، وقد تحاول البرامج بعد ذلك dynamic_cast أ std::type_info لآخر ، النوع المشتق من قبل التنفيذ. ربما هذا هو السبب ، ولكن يبدو أنه من السهل على التطبيقات إضافة عضو محدد من قبل التنفيذ ، والذي قد يكون افتراضيًا. البرامج التي ترغب في اختبار هذه الامتدادات لن تكون بالضرورة غير قابلة للحمل على أي حال.
  2. إنه لضمان تدمير الأنواع المشتقة بشكل صحيح عندما deleteجي مؤشر قاعدة. ولكن لا توجد أنواع مشتقة قياسية ، لا يمكن للمستخدمين تحديد أنواع مشتقة مفيدة ، لأنها type_info لا يوجد لديه منشئون عامون قياسيون ، وهكذا deleteجي type_info المؤشر ليس قانونيًا ومحمولًا أبدًا. والأنواع المشتقة ليست مفيدة لأنها لا يمكن بناؤها - الاستخدام الوحيد الذي أعرفه لمثل هذه الأنواع المشتقة غير القابلة للبناء هو في تنفيذ أشياء مثل is_polymorphic اكتب سمة.
  3. يترك مفتوحة إمكانية metaclasses مع أنواع مخصصة - كل متعدد الأشكال class A سوف تحصل على "metaclass" مشتقة A__type_info, التي تستمد من type_info. ربما يمكن أن تعرض مثل هذه الفصول المشتقة الأعضاء تلك الاتصال new A مع مختلف حجج المنشئ بطريقة آمنة من النوع ، وأشياء من هذا القبيل. لكن صنع type_info تعدد الأشكال نفسه في الواقع يجعل مثل هذه الفكرة مستحيلة بشكل أساسي ، لأنه يجب أن يكون لديك metaclasses ل metaclasses ، adinitum ، وهذا يمثل مشكلة إذا كان كل type_info الكائنات لها مدة تخزين ثابت. ربما باستثناء هذا هو السبب لجعله متعدد الأشكال.
  4. هناك بعض الفائدة لتطبيق ميزات RTTI (بخلاف ذلك dynamic_cast) إلى std::type_info نفسه ، أو شخص ما اعتقد أنه كان لطيفًا ، أو محرجًا إذا type_info لم يكن متعدد الأشكال. ولكن بالنظر إلى أنه لا يوجد نوع مشتق قياسي ، ولا توجد فئات أخرى في التسلسل الهرمي القياسي والتي يمكن للمرء أن يجربها بشكل معقول ، فإن السؤال هو: ماذا؟ هل هناك فائدة للتعبيرات مثل typeid(std::type_info) == typeid(typeid(A))?
  5. ذلك لأن المنفذين سيقومون بإنشاء نوع مستمد خاص بهم (كما أعتقد أن GCC تفعل). ولكن ، لماذا تهتم بتحديده؟ حتى إذا لم يتم تحديد المدمر على أنه افتراضي وقرر أحد المنافقون أنه ينبغي أن يكون ، بالتأكيد أن التنفيذ يمكن أن يعلن أنه افتراضي ، لأنه لا يغير مجموعة العمليات المسموح بها على type_info, ، لذلك لن يتمكن البرنامج المحمول من معرفة الفرق.
  6. إنه شيء يتعلق بالمترجمين الذين يعانون من ABIs متوافق جزئيًا ، ربما نتيجة للربط الديناميكي. ربما يمكن للمنفذي التعرف على خاصة بهم type_info الفئة الفرعية (على عكس واحدة تنشأ من بائع آخر) بطريقة محمولة إذا type_info كان مضمونا لتكون افتراضية.

آخر واحد هو الأكثر منطقية بالنسبة لي في الوقت الحالي ، لكنه ضعيف للغاية.

هل كانت مفيدة؟

المحلول

أفترض أنه موجود لراحة المنفذين. يسمح لهم بتحديد ممتد 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 لا يوجد لديه وظائف افتراضية ، يُسمح له أن يكون له مدمرة افتراضية.

ب) تحديد مجموعة البرامج التي يمكن أن تميز سواء type_info هو متعدد الأشكال أم لا ، وحظرهم جميعا. قد لا تكون برامج مفيدة أو مثمرة للغاية ، لا أعرف ، ولكن لحظرها ، عليك أن توصل إلى بعض اللغة القياسية التي تصف الاستثناء المحدد الذي تقوم به للقواعد العادية.

لذلك أعتقد أن المعيار يجب أن يوضح المدمر الافتراضي ، أو حظره. إن جعل الأمر اختياريًا معقدًا للغاية (أو ربما يجب أن أقول ، أعتقد أنه سيتم الحكم عليه بشكل غير ضروري. التعقيد لم يوقف لجنة المعايير في المناطق التي تعتبر فيها جديرة بالاهتمام ...)

إذا تم حظره ، فيمكن أن يكون التنفيذ:

  • إضافة مدمرة افتراضية إلى بعض فئة مشتقة من type_info
  • اشتق جميع كائنات typeinfo من الذي - التي صف دراسي
  • استخدم ذلك داخليًا كطبقة قاعدة متعددة الأشكال لكل شيء

من شأنه أن يحل الموقف الذي وصفته في الجزء العلوي من المنشور ، لكن النوع الثابت من أ typeid سيظل التعبير const std::type_info, ، لذلك سيكون من الصعب على التطبيقات تحديد الإضافات حيث يمكن للبرامج dynamic_cast إلى أهداف مختلفة لمعرفة أي نوع من type_info كائن لديهم في حالة معينة. ربما كان المعيار يأمل في السماح بذلك ، على الرغم من أن التنفيذ يمكن أن يوفر دائمًا متغيرًا typeid مع نوع ثابت مختلف ، أو ضمان أن أ static_cast إلى فئة تمديد معينة ستعمل ، ثم اترك البرنامج dynamic_cast من هناك.

باختصار ، بقدر ما أعرف أن المدمرة الافتراضية قد يكون مفيدًا للمنفذين ، وإزالته لا يكسب أي شخص آخر غير ذلك لن نقضي وقتًا نتساءل عن سبب وجوده ؛-)

*] في الواقع ، لم أظهر ذلك. لقد أثبتت أن برنامجًا غير قانوني ، كل شيء آخر متساوٍ ، يجمع. لكن ربما يمكن للتنفيذ أن ينجح في ذلك من خلال ضمان أن كل شيء غير متساوٍ ، وأنه لا يجمع. تعزيز is_polymorphic ليس محمولًا ، لذلك على الرغم من أنه من الممكن لبرنامج اختبار هذا الفصل هو تعدد الأشكال ، يجب أن يكون ، قد يكون هناك أي وسيلة لبرنامج مطابق لاختبار هذا الفصل ليس تعدد الأشكال ، لا ينبغي أن يكون. على الرغم من أنه حتى لو كان من المستحيل ، فإن إثبات ذلك ، من أجل إزالة سطر واحد من المعيار ، هو الكثير من الجهد.

نصائح أخرى

يقول معيار C ++ ذلك typeid إرجاع كائن من type_info ، أو فئة فرعية محددة من قبل التنفيذ. لذلك ... أعتقد أن هذا هو الجواب إلى حد كبير. لذلك لا أرى لماذا ترفض نقطتك 1 و 2.

الفقرة 5.2.8 الفقرة 1 من قياسي C ++ الحالي:

نتيجة التعبير النوعي هو 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 متعدد الأشكال

حول أبسط معرف "عالمي" يمكنك الحصول عليه في C ++ هو اسم الفصل ، و typeinfo يوفر وسيلة لمقارنة هذا المعرف للمساواة. لكن التصميم محرج ومحدود لدرجة أنك تحتاج إلى الالتفاف بعد ذلك typeinfo في بعض فئات الغلاف ، على سبيل المثال ، لتكون قادرًا على وضع مثيلات في المجموعات. فعل أندريه ألكساندرسكو ذلك في "تصميمه الحديث C ++" وأعتقد أن ذلك typeinfo الغلاف جزء من مكتبة لوكي. من المحتمل أن يكون هناك واحد أيضًا في Boost ؛ ومن السهل جدًا لفك ، على سبيل المثال ، انظر غلاف بلدي.

ولكن حتى بالنسبة لمثل هذا الغلاف ، لا يوجد بشكل عام أي حاجة لمدمار افتراضي في typeinfo.

وبالتالي ، فإن السؤال ليس "هاه ، لماذا يوجد مدمرة افتراضية" ، بل بالأحرى ، كما أراها ، "هاه ، لماذا التصميم متخلف ، محرج وغير قابل للاستخدام مباشرة"؟ وقد وضعت ذلك في عملية التقييس. على سبيل المثال ، لا تعد IoStreams أمثلة بالضبط على التصميم الرائع ، إما ؛ لا شيء لمحاكاة.

3/ يترك مفتوحة إمكانية metaclass مع أنواع مخصصة - كل متعدد الأشكال class A سوف تحصل على "metaclass" مشتقة A__type_info, التي تستمد من type_info. ربما يمكن أن تعرض مثل هذه الفصول المشتقة الأعضاء تلك الاتصال new A مع مختلف حجج المنشئ بطريقة آمنة من النوع ، وأشياء من هذا القبيل. لكن صنع type_info تعدد الأشكال نفسه في الواقع يجعل مثل هذه الفكرة مستحيلة بشكل أساسي ، لأنه يجب أن يكون لديك metaclasses ل metaclasses ، adinitum ، وهذا يمثل مشكلة إذا كان كل type_info الكائنات لها مدة تخزين ثابت. ربما باستثناء هذا هو السبب لجعله متعدد الأشكال.

ماهر...

على أي حال ، أنا لا أتفق مع هذا المنطق: يمكن بسهولة استبعاد فئات التعريف للأنواع المستمدة من الأنواع المستمدة type_info, ، بما فيها type_info بحد ذاتها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top