Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

التمرير المثالي

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 84 من 104.

عند كتابة template function تقبل argument وتمررها إلى function أخرى، تظهر مشكلة: كيف تحافظ على ما إذا كان argument الأصلي هو lvalue أم rvalue؟ يحل التمرير المثالي هذه المشكلة من خلال تمرير argument تمامًا كما تم استلامها، مع الحفاظ على فئة القيمة الخاصة بها.

العناصر الأساسية هي forwarding references (تُكتب بصيغة T&& في سياق القوالب) وstd::forward. يمكن لـ forwarding reference الارتباط بكلٍّ من lvalues وrvalues، وتُجري std::forward تحويلًا مشروطًا لـ argument إلى نوعه الأصلي:

#include <iostream>
#include <utility>

void process(int& x) { std::cout << "lvalue: " << x << "\n"; }
void process(int&& x) { std::cout << "rvalue: " << x << "\n"; }

template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));
}

int main() {
    int n = 10;
    wrapper(n);       // يستدعي process(int&) - يتم الحفاظ على lvalue
    wrapper(20);      // يستدعي process(int&&) - يتم الحفاظ على rvalue
}

من دون std::forward، تكون المعاملات المُسمّاة دائمًا من نوع lvalues داخل الـ function، حتى إذا تم تمرير rvalue. وهذا سيمنع دلالات النقل من العمل بشكل صحيح عند التمرير إلى المُنشئات أو إلى functions أخرى.

يُعدّ التمرير المثالي (forwarding) ضروريًا لكتابة دوال Factory، وclass wrapper، وأي code عام يحتاج إلى إنشاء objects أو استدعاء function مع الحفاظ على نية المستدعي فيما يتعلق بـ move مقابل copy.

challenge icon

التحدي

سهل

لنَبْنِ دالة مصنع توضّح التمرير المثالي من خلال إنشاء كائنات مع الحفاظ على فئة القيمة للوسائط المُمرَّرة إليها. ستنشئ دالة عامة make_object تمرّر الوسائط إلى مُنشئ الفئة، مما يضمن نسخ قيم lvalue ونقل قيم rvalue.

ستنظّم التعليمة البرمجية عبر ثلاثة ملفات:

  • Widget.h: عرّف فئة Widget تتتبّع كيفية استلامها لبياناتها.

    يجب أن تخزّن Widget اسمًا من النوع std::string وقيمة من النوع int. وفّر مُنشئَين:

    • مُنشئ يأخذ const std::string& (مرجع lvalue) وint: ويطبع Widget constructed (copy): [name]
    • مُنشئ يأخذ std::string&& (مرجع rvalue) وint: ويطبع Widget constructed (move): [name] وينقل السلسلة النصية

    أضف أيضًا أسلوب display() يطبع [name]: [value].

  • Factory.h: أنشئ دالة مصنع للتمرير المثالي.

    اكتب دالة قالب make_object تقبل مرجع تمرير للاسم وint عاديًا للقيمة. استخدم std::forward لتمرير وسيط الاسم إلى مُنشئ Widget مع الحفاظ على فئة قيمته الأصلية.

    يجب أن تعيد الدالة كائن Widget المُنشأ بالقيمة.

  • main.cpp: اقرأ مُدخلين:
    1. اسمًا (سلسلة نصية)
    2. قيمة (عددًا صحيحًا)

    وضّح التمرير المثالي من خلال إنشاء كائنَي Widget:

    1. أولًا، خزّن الاسم في متغير محلي ومرّره إلى make_object باعتباره lvalue
    2. ثانيًا، مرّر سلسلة نصية مؤقتة (الاسم نفسه مضافًا إليه "_temp") مباشرةً إلى make_object باعتبارها rvalue

    بعد إنشاء كل كائن، استدعِ أسلوب display() الخاص به.

على سبيل المثال، مع المُدخلين Gadget و42:

Widget constructed (copy): Gadget
Gadget: 42
Widget constructed (move): Gadget_temp
Gadget_temp: 42

ومع المُدخلين Device و100:

Widget constructed (copy): Device
Device: 100
Widget constructed (move): Device_temp
Device_temp: 100

الفكرة الأساسية هي أن دالة make_object لا تعرف ما إذا كانت قد تلقّت lvalue أم rvalue، ولكن باستخدام std::forward فإنها تحافظ على تلك المعلومة عند استدعاء مُنشئ Widget. يتيح ذلك لتحليل تحميلات المُنشئ الزائدة اختيار الإصدار المناسب، مما يفعّل عمليات النقل الفعّالة للقيم المؤقتة، مع نسخ المتغيرات المُسمّاة بأمان.

جرّب بنفسك

#include <iostream>
#include <string>
#include "Widget.h"
#include "Factory.h"

using namespace std;

int main() {
    // اقرأ الإدخال
    string name;
    int value;
    cin >> name >> value;
    
    // TODO: أنشئ الـ widget الأول بتمرير name كـ lvalue
    // خزّن الـ name في متغير محلي ومرّره إلى make_object
    
    
    // TODO: استدعِ display() على الـ widget الأول
    
    
    // TODO: أنشئ الـ widget الثاني بتمرير سلسلة نصية مؤقتة كـ rvalue
    // مرّر (name + "_temp") مباشرة إلى make_object
    
    
    // TODO: استدعِ display() على الـ widget الثاني
    
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت