لماذا يتم طباعة “A::print()” مرتين عند تشغيل الكود؟ هذا السؤال يتعلق باللامبدا وطريقة التقاط القيم فيها. في الكود الخاص بك، قمت بتعريف دالة call_print
في الفئة A
التي تقوم بتعريف واستدعاء لامبدا print_func
المخزنة في متغير محلي ثابت. هذه اللامبدا تقوم بطباعة الدالة print
المحددة ضمن الكائن الحالي this
، والذي يجب أن يكون مؤشرًا إلى كائن من الفئة A
.
عندما تقوم بإنشاء كائنات A
و B
وتستدعي call_print
على كلا الكائنين، يتم استدعاء اللامبدا مرتين، ويتم تقاطع قيمة this
في كل مرة. السبب في ذلك هو أنه عند تعريف اللامبدا باستخدام [this]
، فإنه يتم التقاط this
بقيمته الحالية في نقطة تعريف اللامبدا، وليس في وقت استدعاء اللامبدا. وبما أن print_func
هي متغير محلي ثابت، فإن اللامبدا تقترن دائمًا بـ this
من الكائن الذي تم إنشاؤه أول مرة.
لحل هذه المشكلة والحصول على النتائج التي تتوقعها، يمكنك تغيير اللامبدا لتأخذ this
كمعامل، بدلاً من التقاطه بشكل ضمني. يمكنك القيام بذلك عن طريق تعديل اللامبدا كما يلي:
cppstatic auto const print_func = [](A* self) {
self->print();
};
ثم استخدامها في الطريقة call_print
بالشكل التالي:
cppprint_func(this);
بهذه الطريقة، يتم تمرير this
كمعامل إلى اللامبدا، وبالتالي سيتم استخدام this
الخاص بالكائن الحالي الذي يتم استدعاء call_print
عليه.
المزيد من المعلومات
بالطبع! لامبدا (Lambda) في C++ هي وظيفة مجهولة الاسم (anonymous function) يمكن تعريفها في مكان واحد واستخدامها في مكان آخر. تُستخدم اللامبدا بشكل شائع لتمرير وظائف بين الدوال أو تخزينها في متغيرات. واحدة من ميزات اللامبدا في C++ هي قدرتها على التقاط (Capture) المتغيرات من النطاق الخارجي.
هناك ثلاثة أنواع من التقاط المتغيرات في اللامبدا في C++:
-
التقاط بالقيمة (
[=]
): يتم نسخ قيم المتغيرات من النطاق الخارجي واستخدامها داخل اللامبدا. على سبيل المثال:cppint x = 10; auto lambda = [=]() { return x; };
-
التقاط بالمرجع (
[&]
): يتم التقاط المتغيرات بواسطة المرجع، مما يعني أن أي تغيير في المتغيرات داخل اللامبدا سيؤثر على النطاق الخارجي أيضًا. على سبيل المثال:cppint y = 20; auto lambda = [&]() { return y; };
-
التقاط محدد (
[x]
أو[&x]
): يتم التقاط متغير محدد بالقيمة أو بالمرجع، على التوالي. على سبيل المثال:cppint z = 30; auto lambda = [z]() { return z; }; // التقاط بالقيمة auto lambda2 = [&z]() { return z; }; // التقاط بالمرجع
أما بالنسبة لسؤالك الأصلي، فإن استخدام [this]
في التقاط اللامبدا يؤدي إلى التقاط this
بقيمته الحالية في نقطة تعريف اللامبدا، وليس في وقت استدعاء اللامبدا.