من هو من في C++: lvalue، rvalue، xvalue و"قيم" أخرى

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما الذي تخفيه الحروف l، r، x، gl و pr؟ فهم فئات القيم في C++

\n\n

إذا كنت قد كتبت يومًا كودًا بلغة C++ وحصلت على خطأ مثل "cannot bind non-const lvalue reference to an rvalue"، فقد واجهت هذا "السحر". فئات القيم — lvalue، rvalue، xvalue، glvalue و prvalue — ليست مجرد تصنيف أكاديمي. إنها الأساس الذي تقوم عليه دلالات النقل (move semantics) والنقل المثالي (perfect forwarding).

\n\n

دعنا نفهم مرة واحدة وإلى الأبد ما يعنيه كل حرف، وكيفية التمييز بينها عمليًا، ولماذا بدون هذه المعرفة لا يمكن كتابة كود C++ حديث وفعال.

\n\n

تاريخ موجز: من أين أتت هذه المصطلحات الخمسة؟

\n\n

في البداية، في زمن C، كان هناك مفهومان فقط: lvalue (القيمة اليسرى — ما يمكن أن يكون على يسار عملية الإسناد) وrvalue (القيمة اليمنى — ما يكون على اليمين). كان الأمر بسيطًا: المتغير هو lvalue، والرقم هو rvalue.

\n\n

مع ظهور C++11 ودلالات النقل، لم يعد هذا التصور البسيط كافيًا. ظهرت مراجع rvalue (&&) والحاجة إلى التمييز بين الكائنات المؤقتة "المحتضرة". هكذا وُلد التصنيف الجديد، الذي يصفه معيار C++ من خلال خمس فئات.

\n\n

ألقِ نظرة على التسلسل الهرمي (رسم بياني نصي):

\n\n
        expression
        /        \\\
   glvalue      rvalue
   /      \\    /      \\\
lvalue   xvalue   prvalue
\n\n

احفظ هذه الصورة — فهي المفتاح لكل شيء.

\n\n

lvalue (left value) — الكائنات "الباقية"

\n\n

lvalue هو تعبير يشير إلى كائن له عنوان في الذاكرة. هذا الكائن لن يختفي بعد انتهاء التعبير. يمكنك أخذ عنوانه، وتعديله، وسيستمر في الوجود.

\n\n

أمثلة على lvalue:

\n
    \n
  • اسم المتغير: int x = 5; x — هو lvalue.
  • \n
  • المؤشر المُشار إليه: *ptr.
  • \n
  • مرجع إلى متغير: int& ref = x; ref.
  • \n
  • النص الحرفي: "hello" (نعم، هو lvalue، وإن كان ثابتًا).
  • \n
\n\n

الكود:

\n
int main() {\n    int a = 10;\n    int* p = &a;   // أخذ العنوان — صحيح، a هو lvalue\n    int& ref = a;  // مرجع إلى lvalue\n\n    // &(a + 1);   // خطأ! a+1 هو rvalue، لا يوجد عنوان\n    return 0;\n}
\n\n

rvalue (right value) — القيم "التي تُستخدم لمرة واحدة"

\n\n

rvalue هو تعبير ليس له عنوان دائم في الذاكرة. إنه موجود فقط في لحظة حساب التعبير. لا يمكنك أخذ عنوانه (باستثناءات نادرة).

\n\n

تشمل rvalue:

\n
    \n
  • الحرفيات (باستثناء النصوص): 42، 3.14f، true.
  • \n
  • نتائج العمليات الحسابية: a + b.
  • \n
  • الكائنات المؤقتة: std::string("temp").
  • \n
  • تعبيرات لامدا.
  • \n
\n\n

الكود:

\n
int foo() { return 5; }\n\nint main() {\n    int x = 42;       // 42 — rvalue\n    int y = x + 10;   // x+10 — rvalue\n    // int* p = &(x + 10); // خطأ! لا يمكن أخذ عنوان rvalue\n    return 0;\n}
\n\n

prvalue (pure rvalue) — rvalue "النقية"

\n\n

prvalue هو rvalue الكلاسيكي من C++98. إنها قيمة مؤقتة ليس لها اسم ولا ترتبط بذاكرة. prvalue هو ما نسميه عادةً rvalue في الحديث العام.

\n\n

أمثلة على prvalue:

\n
    \n
  • الأرقام، التعدادات، نتائج العوامل.
  • \n
  • استدعاء دالة تُرجع غير مرجع: std::to_string(123).
  • \n
  • كائن مؤقت تم إنشاؤه صراحةً: MyClass().
  • \n
\n\n

xvalue (expiring value) — الكائنات "المحتضرة"

\n\n

هنا يبدأ الجزء المثير للاهتمام. xvalue هو كائن سيتم تدميره قريبًا، ولكن لا يزال من الممكن "إنقاذ" موارده (نقلها). هذا هو الجس

المدونات

توصيات الكتب