
ملخص الأجزاء السابقة
نظرًا للقيود المفروضة على القدرة على استخدام برامج التحويل البرمجي لـ C ++ 11 ، وبسبب عدم وجود بدائل ، أراد التعزيز كتابة تطبيقه الخاص لمكتبة C ++ 11 القياسية أعلى مكتبة C ++ 98 / C ++ 03 المزودة مع برنامج التحويل البرمجي.
تم تنفيذ
Static_assert ،
noexcept ،
countof ، وأيضًا بعد النظر في جميع
التعريفات غير القياسية وميزات المترجم ، ظهرت معلومات حول الوظيفة التي يدعمها المترجم الحالي.
يتم تضمين تطبيقه الخاص لـ
nullptr ، والذي يتم اختياره في مرحلة التجميع.
لقد حان الوقت
لنوع type_traits وكل هذا "سحر القالب الخاص".
رابط إلى GitHub بالنتيجة لهذا اليوم للصبر وغير القراء:
نرحب بالالتزامات والنقد البناء
انغمس في عالم "قالب السحر" C ++.
جدول المحتويات
مقدمةالفصل 1. فيام الإشراف على vadensالفصل 2. #ifndef __CPP11_SUPPORT__ # تعريف __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifالفصل 3. إيجاد تنفيذ nullptr الكمالالفصل 4. C ++ قالب سحري....
4.1 نبدأ صغيرة....
4.2 حول عدد الأخطاء المعجزة التي يجمعها لنا السجل....
4.3 المؤشرات والجميع....
4.4 ما هو مطلوب أيضًا لمكتبة النماذجالفصل الخامس
...
الفصل 4. C ++ قالب سحري
بعد الانتهاء من الكلمات الأساسية لـ C ++ 11 وجميع "المفاتيح" المعتمدة على التعريف بين عمليات التنفيذ ، بدأت في ملء
type_traits . في الحقيقة ، كان لدي بالفعل عدد قليل جدًا من فئات القوالب ، على غرار الفئات القياسية ، التي عملت بالفعل في المشاريع لفترة طويلة ، وبالتالي بقي إدخال كل هذا في نفس النموذج ، بالإضافة إلى إضافة الوظائف المفقودة.

بصراحة ، أنا مستوحى من برمجة القوالب. خاصةً إدراك أن كل هذا عبارة عن مجموعة متنوعة من الخيارات: الحسابات ، تفريع الرمز ، الشروط ، التحقق من الأخطاء يتم إجراؤها أثناء عملية التجميع ولا شيء يكلف البرنامج النهائي في وقت التشغيل. وبما أن القوالب في C ++ هي في الأساس
لغة برمجة كاملة من تورينج ، كنت أتوقع كيف سيكون من السهل والأنيق تنفيذ جزء من المعيار المتعلق بالبرمجة على القوالب. ولكن ، من أجل تدمير جميع الأوهام على الفور ، سأقول أن نظرية اكتمال تورينج مقسمة إلى تطبيقات ملموسة للنماذج في المترجمات. وهذا الجزء من كتابة المكتبة ، بدلاً من الحلول الأنيقة و "الحيل" لبرمجة القوالب ، تحول إلى صراع شرس مع المترجمين ، بينما "انهار" كل واحد بطريقته الخاصة ، ومن الجيد إذا ذهب إلى خطأ داخلي متراكم في المترجم ، أو حتى تحطم بإحكام استثناءات غير معالجة. أظهر GCC (g ++) نفسه على أفضل وجه ، والذي "يمضغ" بشكل صريح جميع
تركيبات القوالب ويلعن فقط (في الحالة) في الأماكن التي كان هناك نقص في
اسمها الصريح.
4.1 البداية صغيرة
لقد بدأت بقوالب بسيطة لـ
std :: Integral_constant و
std :: bool_constant وقوالب صغيرة مماثلة.
template<class _Tp, _Tp Val> struct integral_constant {
بناءً على
الشروط الشرطية ، يمكنك إدخال نماذج مناسبة للعمليات المنطقية {"و" أو "أو" ، "لا"} عبر الأنواع (وتعتبر جميع هذه العمليات مناسبة في مرحلة التجميع! إنها رائعة ، أليس كذلك؟):
namespace detail { struct void_type {};
هناك ثلاث نقاط تستحق الاهتمام هنا:
1) من المهم دائمًا وضع مسافة بين أقواس الزاوية ('<' و '>') من القوالب ، لأنه قبل C ++ 11 لم يكن هناك توضيح في المعيار حول كيفية تفسير ">>" و "<<" في التعليمات البرمجية مثل _or _ <_ B2 ، _or _ <_ B3، _B4 >> ، وبالتالي تعامل جميع المترجمين تقريبًا مع هذا كعامل تبديل بت ، مما يؤدي إلى خطأ في الترجمة .
2) في بعض المترجمين (Visual Studio 6.0 على سبيل المثال) كان هناك خطأ يتألف من حقيقة أنه كان من المستحيل استخدام نوع الفراغ كمعلمة قالب. لهذه الأغراض ، يتم إدخال نوع void_type منفصل في المقطع أعلاه لاستبدال نوع الفراغ حيث تكون قيمة معلمة القالب الافتراضية مطلوبة.
3) المترجمون القدامى جدًا (Borland C ++ Builder على سبيل المثال) لديهم نوع منطقي تم تنفيذه بشكل مخادع ، والذي تحول في بعض الحالات "فجأة" إلى int ( صحيح -> 1 ، خطأ -> 0) ، بالإضافة إلى أنواع المتغيرات الثابتة الثابتة من النوع منطقية (وليس فقط) ، إذا كانت مضمنة في فئات القالب. نتيجة لكل هذه الفوضى ، نتيجة لذلك ، لمقارنة غير ضارة تمامًا في أسلوب my_template_type :: static_bool_value == false ، يمكن للمترجم بسهولة إصدار ساحر لا يمكنه إرسال "نوع غير محدد" إلى int (0) أو شيء من هذا القبيل. لذلك ، من الضروري دائمًا محاولة الإشارة بوضوح إلى نوع القيم للمقارنة ، وبالتالي مساعدة المترجم على تحديد الأنواع التي يتعامل معها.
أضف المزيد من العمل بقيم
ثابتة ومتغيرة . أولاً ،
الإزالة التي تم تنفيذها بشكل
تافه ... حيث نقوم ببساطة
بتخصيص القالب لمعدلات نوع معينة - إذا كان النوع الذي يحتوي على التعديل في القالب ، يجب على المترجم ، بعد النظر في جميع التخصصات (تذكر مبدأ SFINAE من
الفصل السابق ) من القالب ، حدد الأنسب (مع إشارة صريحة للمعدل المطلوب) :
template<class _Tp> struct is_function; template<class _Tp> struct remove_const {
ثم نقوم بتطبيق قوالب
add_ ... حيث يكون كل شيء أكثر تعقيدًا بالفعل:
namespace detail { template<class _Tp, bool _IsFunction> struct _add_const_helper { typedef _Tp const type; }; template<class _Tp> struct _add_const_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_volatile_helper { typedef _Tp volatile type; }; template<class _Tp> struct _add_volatile_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_cv_helper { typedef _Tp const volatile type; }; template<class _Tp> struct _add_cv_helper<_Tp, true> { typedef _Tp type; }; }
نقوم هنا بمعالجة الأنواع المرجعية بشكل منفصل حتى لا نفقد الرابط. أيضا ، لن ننسى أنواع الوظائف التي من المستحيل جعلها
متقلبة أو
ثابتة من حيث المبدأ ، لذلك سنتركها "كما هي". يمكنني القول أن كل هذا يبدو بسيطًا جدًا ، ولكن هذا هو الحال تمامًا عندما يكون "الشيطان في التفاصيل" ، أو بالأحرى ، "الخلل في تفاصيل التنفيذ".
نهاية الجزء الأول من الفصل الرابع. في
الجزء الثاني سأتحدث عن كيفية إعطاء برمجة القالب الثابت للمترجم ، وسيكون هناك أيضًا المزيد من سحر القالب الرائع. آه ، ومع ذلك - لماذا
طويلة ليست
ثابتة لا يتجزأ ، وفقا لبعض المجمعين حتى يومنا هذا.
شكرا لكم على اهتمامكم.