مساعدة المحول البرمجي C ++ في حل overloading الدالة

في بعض الحالات ، يتعذر على برنامج التحويل البرمجي لـ C ++ تحديد وظيفة التحميل الزائد المناسبة ، على سبيل المثال ، في موقف يتضح من وجهة نظر بشرية - يحدث خطأ في الترجمة:

void f(int i){} void f(string s){} vector<int> int_c = { 1, 2, 3, 4, 5 }; vector<string> string_c = { "Sic" ,"transit" ,"gloria" ,"mundi" }; for_each(begin(int_c), end(int_c), f);//error C2672: "for_each":      

المشكلة هي نقص المعلمات f في السطر الأخير ، والتي يمكن للمترجم حل الزائد. حقيقة أن التكرار من نوع محدد للغاية يتم تمرير معلمات for_each لا يهم للمترجم.

يمكنك تقديم عدة طرق "أمامية" لحل المشكلة:

1) استخدام static_cast <> () لفرض المصبوب على مؤشر دالة من النوع المطلوب.

نحن يلقي بالمؤشر لإلغاء f (int i):

 std::for_each(begin(int_c), end(int_c), static_cast<void (*)(int)>(&f)); 

نحن يلقي بالمؤشر لإلغاء f (السلسلة i):

 std::for_each(begin(string_c), end(string_c), static_cast<void (*)(string)>(&f)); 

2) تعريف المؤشرات إلى الوظائف واستخدامها.

نستخدم الفراغ f (int i):

  void (*fp_int)(int) = &f; for_each(begin(int_c), end(int_c), fp_int); 

نستخدم الفراغ f (السلسلة i):

 void (*fp_string)(string) = &f; for_each(begin(string_c), end(string_c), fp_string); 

3) إنشاء تخصصات واضحة لوظيفة القالب for_each:

نستخدم الفراغ f (int i):

 std::for_each<vector<int>::const_iterator, void(*)(int) >(int_c.begin(), int_c.end(), f); 

نستخدم الفراغ f (السلسلة i):

 std::for_each<vector<string>::const_iterator, void(*)(string) >(string_c.begin(), string_c.end(), f); 

أو أكثر إحكاما:

 std::for_each<decltype(int_c.begin()), void(*)(int) >(int_c.begin(), int_c.end(), f); std::for_each<decltype(string_c.begin()), void(*)(string) >(string_c.begin(), string_c.end(), f); 

الأساليب المقترحة أعلاه ليست عالمية وتؤدي إلى الحاجة إلى تطوير رمز إضافي على جانب الاتصال. أفضل فكرة هي إنشاء قشرة رقيقة فوق الوظيفة f والتي تأخذ معلمات الدالة f وتمريرها إليها دون تغييرات.

من الضروري أيضًا النظر في الاحتمالات:

  • وجود عدد غير محدد من معلمات الدالة f ،
  • وجود القيمة المرجعة للدالة ،
  • تمرير المعلمات حسب المرجع ،
  • وجود تصفيات المعلمات السيرة الذاتية ،
  • استثناءات في الوظيفة f.

خطوة بخطوة سنقوم بتحليل بناء مثل هذه قذيفة. وظيفة لامدا هي أساس جيد. بدعم من C ++ 11 القياسي من قبل المترجم ، يمكن تنفيذ هذا على النحو التالي:

 for_each(begin(int_c), end(int_c), [](int a) { return f(a); }); for_each(begin(string_c), end(string_c), [](string a) { return f(a); }); 

أو باستخدام Dectype ():

 for_each(begin(int_c), end(int_c), [](decltype(*int_c.begin()) a) { return f(a); }); for_each(begin(string_c), end(string_c), [](decltype(*string_c.begin()) a) { return f(a); }); 

بدعم من المعيار C ++ 14 من قبل المترجم ، لدينا الحل التالي للحصول على لامدا متعدد الأشكال:

 auto l = [](auto a) { return f(a); }; for_each(begin(int_c), end(int_c), l); for_each(begin(string_c), end(string_c), l); 

لذلك ، نقوم بتمرير المعلمة إلى الدالة f كمعلمة إلى lambda ، ثم ندعو f مع هذه المعلمة. ولكن هناك مشكلة: لا نعرف مقدمًا - هل معلمات الدالة f القيم أو المراجع؟ لذلك ، يجب عليك استخدام العتاد المثالي.

 auto l = [](auto&& a) { return f(std::forward<decltype(a)>(a)); }; for_each(begin(int_c), end(int_c), l); for_each(begin(string_c), end(string_c), l); 

حول موضوع انتقال مثالي (إعادة توجيه مثالية) يمكن العثور عليها في مايرز [1]. يمكن الاطلاع على نسخة أكثر قابلية للفهم (بالنسبة لي ، على أي حال) من عرض نفس المادة في المادة [2] ، وترجمتها على حبري [3].

بالنسبة لعدد غير محدد من معلمات الدوال ، فإننا نشير إلى معلمات lambda كمتغير قادر وندعو std :: forward لكل معلمة تنتقل إلى f.

  auto l = [](auto&&... a) { return f(std::forward<decltype(a)>(a)...); }; 

نضيف noexcept لمحدِّد وقت برنامج التحويل البرمجي [4] ومشغل noexcept [5] للإشارة إلى المترجم ما إذا كانت الدالة f ستلقي استثناءات [6].

  auto l = [](auto&&... a) \ noexcept(noexcept(f(std::forward<decltype(a)>(a)...)))\ { return f(std::forward<decltype(a)>(a)...); }; 

إضافة إخراج نوع القيمة المرجعة لامدا شيدت.

  auto l = [](auto&&... a) \ noexcept(noexcept(f(std::forward<decltype(a)>(a)...)))\ -> decltype(f(std::forward<decltype(a)>(a)...))\ { return f(std::forward<decltype(a)>(a)...); }; 

إذا كان تكرار الرمز الثلاثي مزعجًا ولا نتردد في استخدام وحدات الماكرو - فيمكنك تقليل الحجم في جانب الاتصال.

 #define LIFT(foo) \ [](auto&&... x) \ noexcept(noexcept(foo(std::forward<decltype(x)>(x)...))) \ -> decltype(foo(std::forward<decltype(x)>(x)...)) \ { return foo(std::forward<decltype(x)>(x)...); } 

كنتيجة لذلك ، لدينا غلاف يساعد المترجم على حل دالة overloaded.

 for_each(begin(int_c), end(int_c), LIFT(f)); for_each(begin(string_c), end(string_c), LIFT(f)); 

[1] س. مايرز "C ++ الحديثة الفعالة" البند 24: تمييز المراجع العالمية من مراجع rvalue.
[2] eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c
[3] habr.com/en/post/242639
[4] en.cppreference.com/w/cpp/language/noexcept_spec
[5] en.cppreference.com/w/cpp/language/noexcept
[6] habr.com/en/post/164221
[7] www.fluentcpp.com 2017/08/01 / Overloaded-functions-stl
[8] www.youtube.com/watch؟v=I3T4lePH-yA

Source: https://habr.com/ru/post/ar485854/


All Articles