في بعض الحالات ، يتعذر على برنامج التحويل البرمجي لـ 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