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) टिकी हुई है।
आइए एक बार और हमेशा के लिए समझ लें कि हर अक्षर का क्या मतलब है, व्यवहार में उन्हें कैसे पहचानें, और क्यों इस ज्ञान के बिना आधुनिक, कुशल C++ लिखना असंभव है।
\n\nसंक्षिप्त इतिहास: ये पाँच शब्द कहाँ से आए?
\n\nशुरुआत में, C के समय में, केवल दो अवधारणाएँ थीं: lvalue (बायाँ मान — जो असाइनमेंट के बाईं ओर आ सकता है) और rvalue (दायाँ मान — जो दाईं ओर आता है)। सब कुछ सरल था: वेरिएबल lvalue है, संख्या rvalue है।
\n\nC++11 और मूव सेमेन्टिक्स के आगमन के साथ, यह सरल तस्वीर काम नहीं आई। rvalue रेफरेंस (&&) और 'मरने वाली' अस्थायी वस्तुओं (temporary objects) के बीच अंतर करने की आवश्यकता पैदा हुई। इस तरह नया वर्गीकरण जन्मा, जिसे C++ मानक पाँच श्रेणियों में वर्णित करता है।
इस पदानुक्रम (टेक्स्ट के रूप में डायग्राम) पर एक नज़र डालें:
\n\n expression (अभिव्यक्ति)\n / \\\n glvalue rvalue\n / \\ / \\\nlvalue xvalue prvalue\n\nइस तस्वीर को याद रखें — यह सबकी कुंजी है।
\n\nlvalue (left value) — 'लंबे समय तक जीने वाली' वस्तुएँ
\n\nlvalue एक ऐसी अभिव्यक्ति है जो मेमोरी में एक पता (address) रखने वाली वस्तु को संदर्भित करती है। ऐसी वस्तु अभिव्यक्ति के समाप्त होने के बाद गायब नहीं होती। आप इसका पता ले सकते हैं, इसे बदल सकते हैं, और यह अस्तित्व में बनी रहेगी।
\n\nlvalue के उदाहरण:
\n- \n
- वेरिएबल का नाम:
int x = 5; x— यह lvalue है। \n - डीरेफरेंस किया गया पॉइंटर:
*ptr। \n - वेरिएबल का रेफरेंस:
int& ref = x; ref। \n - स्ट्रिंग लिटरल:
"hello"(हाँ, यह lvalue है, भले ही कॉन्स्टेंट हो)। \n
कोड:
\nint 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\nrvalue (right value) — 'एक बार उपयोग होने वाले' मान
\n\nrvalue एक ऐसी अभिव्यक्ति है जिसका मेमोरी में कोई स्थायी पता नहीं होता। यह केवल अभिव्यक्ति के मूल्यांकन के समय ही अस्तित्व में रहता है। आप इसका पता नहीं ले सकते (कुछ अपवादों को छोड़कर)।
\n\nrvalue में शामिल हैं:
\n- \n
- लिटरल (स्ट्रिंग को छोड़कर):
42,3.14f,true। \n - अंकगणितीय संक्रियाओं के परिणाम:
a + b। \n - अस्थायी वस्तुएँ (temporary objects):
std::string("temp")। \n - लैम्ब्डा एक्सप्रेशन। \n
कोड:
\nint 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\nprvalue (pure rvalue) — 'शुद्ध' rvalue
\n\nprvalue C++98 का क्लासिक rvalue है। यह एक अस्थायी मान है जिसका कोई नाम नहीं है और यह मेमोरी से बंधा नहीं है। बोलचाल की भाषा में हम आमतौर पर prvalue को ही rvalue कहते हैं।
\n\nprvalue के उदाहरण:
\n- \n
- संख्याएँ, एनुमरेशन, ऑपरेटरों के परिणाम। \n
- फंक्शन कॉल जो गैर-रेफरेंस लौटाता है:
std::to_string(123)। \n - स्पष्ट रूप से बनाई गई अस्थायी वस्तु:
MyClass()। \n
xvalue (expiring value) — 'मरने वाली' वस्तुएँ
\n\nयहाँ से सबसे दिलचस्प चीज़ शुरू होती है। xvalue एक ऐसी वस्तु है जो जल्द ही नष्ट होने वाली है, लेकिन इसके संसाधनों को अभी भी 'बचाया' (मूव किया) जा सकता है। यह एक पुल की तरह है