C++11 में T&&: डबल एम्परसेंड का क्या मतलब है? पूर्ण विश्लेषण

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

C++11 में डबल एम्परसेंड के पीछे क्या छिपा है? T&& को समझते हैं

\n\n

यदि आपने कभी C++ में प्रोग्राम लिखा है और C++11 या उससे नए मानक के अनुसार लिखे गए कोड को देखा है, तो आपने निश्चित रूप से अजीब सिंटैक्स देखा होगा: T&&। डबल एम्परसेंड तुरंत ध्यान आकर्षित करता है — क्योंकि पुराने अच्छे C++98/03 में हम इसके आदी थे कि & का मतलब संदर्भ है। और यहाँ तो दो हैं! यह क्या है? संदर्भ का संदर्भ? कंपाइलर की गलती? या इससे कहीं अधिक शानदार कुछ?

\n\n

स्पॉयलर: यह कोई गलती नहीं है। यह आधुनिक C++ की सबसे शक्तिशाली सुविधाओं में से एक है, जिसने प्रदर्शन और संसाधन प्रबंधन के बारे में सोचने के तरीके को बदल दिया है। इस लेख में, हम समझेंगे कि T&& का क्या अर्थ है, यह सामान्य संदर्भ से कैसे अलग है, और इसका सही उपयोग कैसे करें ताकि आपका कोड तेज़ चले, न कि धीमा।

\n\n

हम Stack Overflow के वास्तविक प्रश्नों का उत्तर देंगे जो हजारों डेवलपर्स को परेशान करते हैं, और लाइव कोड उदाहरण दिखाएंगे। चलिए शुरू करते हैं!

\n\n

1. rvalue-संदर्भ का परिचय: एक एम्परसेंड क्यों पर्याप्त नहीं है?

\n\n

शुरुआत में, याद करते हैं कि lvalue और rvalue क्या हैं। संक्षेप में:

\n\n
    \n
  • lvalue (लेफ्ट वैल्यू) — वह ऑब्जेक्ट है जिसका एक नाम और मेमोरी में पता होता है। उदाहरण: int x = 5; — x एक lvalue है।
  • \n
  • rvalue (राइट वैल्यू) — एक अस्थायी मान है जिसका कोई नाम नहीं होता और आमतौर पर एक्सप्रेशन के बाद नष्ट हो जाता है। उदाहरण: 5, x + 2, फंक्शन कॉल का परिणाम getTemp()।
  • \n
\n\n

C++03 में हमारे पास केवल एक संदर्भ था — int& (lvalue-संदर्भ)। यह केवल lvalue से बंध सकता था। किसी फंक्शन में एक अस्थायी ऑब्जेक्ट पास करने का प्रयास करें जो int& की अपेक्षा करता है, और कंपाइलर एक त्रुटि देगा। यह एक सुरक्षा है: अस्थायी ऑब्जेक्ट को बदलना एक बुरा विचार है।

\n\n

लेकिन क्या होगा यदि हम अस्थायी ऑब्जेक्ट से डेटा को स्थानांतरित करना चाहते हैं, न कि कॉपी करना? उदाहरण के लिए, हमारे पास एक स्ट्रिंग std::string है जो किसी फंक्शन से लौटती है। इसे कॉपी करना महंगा है। लेकिन इसके आंतरिक बफर को "चुराना" सस्ता है। इसके लिए ही rvalue-संदर्भ (T&&) की आवश्यकता है।

\n\n
// फंक्शन जो rvalue-संदर्भ स्वीकार करता है\nvoid process(std::string&& str) {\n    std::cout << "अस्थायी स्ट्रिंग को प्रोसेस कर रहे हैं: " << str << std::endl;\n    // यहाँ डेटा को कॉपी करने के बजाय स्थानांतरित किया जा सकता है\n}\n\nint main() {\n    std::string s = "Hello";\n    process(std::move(s)); // std::move lvalue को rvalue में बदलता है\n    // process(s); // त्रुटि! s एक lvalue है, जबकि फंक्शन rvalue की अपेक्षा करता है\n    return 0;\n}
\n\n

अब T&& संदर्भ का संदर्भ नहीं है, बल्कि एक नया प्रकार का संदर्भ है जो केवल rvalue से बंध सकता है। यही मुख्य प्रश्न का उत्तर है: C++11 में T&& का अर्थ rvalue-संदर्भ है।

\n\n

2. मूव सेमैंटिक्स: T&& कोड को कैसे तेज़ बनाता है

\n\n

rvalue-संदर्भों के आने का मुख्य कारण मूव सेमैंटिक्स (move semantics) है। कल्पना करें कि आपके पास 10,000 तत्वों वाला एक वेक्टर है। C++ के पुराने संस्करण में, जब यह किसी फंक्शन से लौटता था, तो इसे कॉपी किया जाता था:

\n\n
std::vector<int> createBigVector() {\n    std::vector<int> v(10000, 42);\n    return v; // C++03 में कॉपी, C++11 में मूव\n}
\n\n

C++11 में, कंपाइलर देखता है कि v एक lvalue है, लेकिन यह समझदार है: यह जानता है कि v जल्द ही नष्ट हो जाएगा, इसलिए यह अंतर्निहित मूव लागू करता है। लेकिन यदि आप स्पष्ट रूप से डेटा को स्थानांतरित करना चाहते हैं, तो std::move का उपयोग करें।

\n\n

डायनामिक मेमोरी वाले एक क्लास पर विचार करें:

\n\n
class MyBuffer {\nprivate:\n    int* data;\n    size_t size;\npublic:\n    // मूव कंस्ट्रक्टर\n    MyBuffer(MyBuffer&& other) noexcept\n        : data(other.data), size(other.size) {\n        other.data = nullptr; // महत्वपूर्ण! स्रोत को वैध स्थिति में छोड़ें\n        other.size = 0;\n    }\n    \n    // सामान्य कंस्ट्रक्टर                

ब्लॉग

पुस्तक अनुशंसाएँ