2012-11-17 14 views
16

एक C++Next blog post से कहा किवापसी rvalue संदर्भ

A compute(…) 
{ 
    A v; 
    … 
    return v; 
} 

तो A एक सुलभ प्रतिलिपि है या निर्माता ले जाते हैं, संकलक प्रतिलिपि छिपाना चुन सकते हैं। अन्यथा, यदि A में एक चालक कन्स्ट्रक्टर है, v स्थानांतरित हो गया है। अन्यथा, यदि A में एक कॉपी कन्स्ट्रक्टर है, v कॉपी किया गया है। अन्यथा, संकलन समय त्रुटि उत्सर्जित होती है।

मैंने सोचा कि मुझे हमेशा std::move के बिना मूल्य वापस करना चाहिए क्योंकि संकलक उपयोगकर्ताओं के लिए सबसे अच्छा विकल्प समझने में सक्षम होगा। लेकिन ब्लॉग से एक और उदाहरण में पोस्ट

Matrix operator+(Matrix&& temp, Matrix&& y) 
    { temp += y; return std::move(temp); } 

यहाँ std::move आवश्यक है, क्योंकि y समारोह के अंदर एक lvalue के रूप में व्यवहार किया जाना चाहिए।

आह, मेरा ब्लॉग लगभग इस ब्लॉग पोस्ट का अध्ययन करने के बाद उड़ा। मैंने तर्क को समझने के लिए अपनी पूरी कोशिश की लेकिन जितना मैंने अध्ययन किया, उतना ही भ्रमित हो गया। हमें std::move की सहायता से मूल्य क्यों वापस करना चाहिए?

+1

हैं कि विवादित लेख का लिंक है? –

+1

[संबंधित एफएक्यू] (http://stackoverflow.com/questions/3106110/) – fredoverflow

+1

http://cpp-next.com/archive/2009/09/making-your-next-move/, यह एक श्रृंखला है rvalue संदर्भ – StereoMatching

उत्तर

21

तो, मान लीजिए कि आपने है:

A compute() 
{ 
    A v; 
    … 
    return v; 
} 

और आप कर रहे हैं:

A a = compute(); 

दो स्थानान्तरण (कॉपी या स्थानांतरित) है कि इस अभिव्यक्ति में शामिल कर रहे हैं। सबसे पहले फ़ंक्शन में v द्वारा निर्दिष्ट ऑब्जेक्ट को फ़ंक्शन के परिणाम में स्थानांतरित किया जाना चाहिए, यानी compute() अभिव्यक्ति द्वारा दान किया गया मान। के कहते हैं कि स्थानांतरण 1. फिर, इस अस्थायी वस्तु वस्तु a से दर्शाया जाता है बनाने के लिए स्थानांतरित कर रहा है - स्थानांतरण 2.

कई मामलों में, दोनों स्थानांतरण 1 और 2 संकलक द्वारा elided जा सकता है - वस्तु v सीधे निर्माण किया है a के स्थान पर और कोई स्थानांतरण आवश्यक नहीं है। कंपाइलर को इस उदाहरण में स्थानांतरण 1 के लिए नामांकित रिटर्न वैल्यू ऑप्टिमाइज़ेशन का उपयोग करना होगा, क्योंकि वापस आने वाली ऑब्जेक्ट का नाम दिया गया है। अगर हम प्रतिलिपि/चाल elision अक्षम करते हैं, हालांकि, प्रत्येक स्थानांतरण में ए के प्रतिलिपि निर्माता या उसके चालक कन्स्ट्रक्टर को कॉल शामिल है। अधिकांश आधुनिक कंपाइलरों में, संकलक देखेंगे कि v नष्ट होने वाला है और यह पहले इसे वापसी मूल्य में ले जाएगा। फिर यह अस्थायी वापसी मूल्य a में ले जाया जाएगा। यदि A में कोई चालक कन्स्ट्रक्टर नहीं है, तो इसकी बजाय प्रतिस्थापन दोनों के लिए कॉपी किया जाएगा।

अब पर नज़र करने देता है:

A compute(A&& v) 
{ 
    return v; 
} 

हम वापस लौट रहे हैं मूल्य संदर्भ समारोह में पारित किया जा रहा से आता है। कंपाइलर यह नहीं मानता कि v एक अस्थायी है और यह से स्थानांतरित करना ठीक है। इस मामले में, स्थानांतरण 1 एक प्रतिलिपि होगी। फिर स्थानांतरण 2 एक कदम होगा - यह ठीक है क्योंकि लौटाया गया मूल्य अभी भी एक अस्थायी है (हमने संदर्भ वापस नहीं किया है)। लेकिन चूंकि हम जानते हैं है कि हम एक वस्तु है कि हम से स्थानांतरित कर सकते हैं लिया है, क्योंकि हमारे पैरामीटर एक rvalue संदर्भ है, हम स्पष्ट रूप से std::move के साथ एक अस्थायी रूप v के इलाज के लिए संकलक बता सकते हैं:

A compute(A&& v) 
{ 
    return std::move(v); 
} 

अब ट्रांसफर 1 और ट्रांसफर 2 दोनों चलेंगे।


कारण है कि संकलक स्वचालित रूप से v, A&& के रूप में परिभाषित का इलाज नहीं करता है, के रूप में एक rvalue सुरक्षा से एक है। इसे समझने के लिए यह बहुत बेवकूफ नहीं है। एक बार ऑब्जेक्ट का नाम हो जाने के बाद, इसे आपके कोड में कई बार संदर्भित किया जा सकता है। पर विचार करें:

A compute(A&& a) 
{ 
    doSomething(a); 
    doSomethingElse(a); 
} 

तो a स्वचालित रूप से एक rvalue के रूप में इलाज किया गया था, doSomething, अपनी हिम्मत बाहर चीर के लिए स्वतंत्र होगा जिसका अर्थ है कि adoSomethingElse को पारित किया जा रहा अमान्य हो सकता है। भले ही doSomething ने मूल्य के आधार पर अपना तर्क लिया, तो ऑब्जेक्ट को आगे ले जाया जाएगा और इसलिए अगली पंक्ति में अमान्य हो जाएगा। इस समस्या से बचने के लिए, नामित रावल संदर्भ संदर्भ हैं। इसका मतलब है कि doSomething कहा जाता है, a सबसे खराब रूप से प्रतिलिपि बनाई जाएगी, अगर केवल lvalue संदर्भ द्वारा नहीं लिया गया - यह अभी भी अगली पंक्ति में मान्य होगा।

यह कहने के लिए compute के लेखक पर है, "ठीक है, अब मैं इस मान को स्थानांतरित करने की अनुमति देता हूं, क्योंकि मुझे पता है कि यह एक अस्थायी वस्तु है"। आप std::move(a) कहकर ऐसा करते हैं। उदाहरण के लिए, आप doSomething एक प्रति दे और फिर इसे से स्थानांतरित करने के लिए अनुमति देते हैं doSomethingElse सकता है:

A compute(A&& a) 
{ 
    doSomething(a); 
    doSomethingElse(std::move(a)); 
} 
+0

क्या आपका मतलब है कि कंपाइलर केवल स्वचालित ऑब्जेक्ट को स्थानांतरित या elide कर सकता है? तो दूसरे मामले में, हमें std :: move का उपयोग करना चाहिए ताकि संकलक को यह समझने में सहायता मिल सके कि && v एक रावल्यू है क्योंकि हमारे मिस्टर कंपाइलर यह समझने के लिए पर्याप्त स्मार्ट नहीं हैं कि हम नहीं जानते हैं अब और जरूरत नहीं है? – StereoMatching

+0

खैर, सत्य यह है कि एक नामित रावल्यू संदर्भ एक लवली अभिव्यक्ति है। अभिव्यक्ति 'v' एक अंतराल है, भले ही इसका प्रकार एक रावल्यू संदर्भ है। ऐसा नहीं है कि संकलक पर्याप्त स्मार्ट नहीं है - इसे एक रावल्यू के रूप में पेश करने के लिए खतरनाक होगा। क्यों मैं समझाने के लिए अपने जवाब में थोड़ा सा जोड़ दूंगा। –

+0

धन्यवाद, मुझे लगता है कि मुझे पता है कि "एक बार फिर" रावल्यू संदर्भ क्या है।यह बात बहुत जटिल है, मुझे आश्चर्य है कि यह सुविधा केवल उन पुस्तकालयों डेवलपर्स और कंपाइलर विक्रेताओं द्वारा उपयोग की जाएगी। – StereoMatching

5

स्वचालित परिणामों के लिए फ़ंक्शन परिणामों का एक निहित कदम केवल संभव है। एक रैल्यू संदर्भ पैरामीटर स्वचालित ऑब्जेक्ट को इंगित नहीं करता है, इसलिए आपको उस मामले में स्पष्ट रूप से कदम का अनुरोध करना होगा।

+0

मुख्य बिंदु यह है कि पहले उदाहरण में 'v' को फ़ंक्शन के अंत में नष्ट किया जा रहा है, इसलिए यह प्रतिलिपि के बजाय एक चाल के रूप में अंतिम ऑपरेशन के लिए समझ में आता है। हालांकि दूसरे उदाहरण में, 'temp' द्वारा निर्दिष्ट ऑब्जेक्ट फ़ंक्शन के अंत में लाइव होगा। –

4

पहला एनवीआरओ का लाभ उठाता है जो आगे बढ़ने से भी बेहतर है। नहीं कॉपी एक सस्ते से बेहतर है।

दूसरा एनवीआरओ का लाभ नहीं उठा सकता है। कोई elision मानते हैं, return temp; कॉपी कन्स्ट्रक्टर को कॉल करेगा और return std::move(temp); चालक कन्स्ट्रक्टर को कॉल करेगा। अब, मेरा मानना ​​है कि इनमें से किसी के पास समान क्षमता होने की समान क्षमता है और इसलिए यदि आप std::move का उपयोग कर रहे हैं तो आपको सस्ता व्यक्ति के साथ जाना चाहिए।

+2

कॉलिंग 'std :: move' एनआरवीओ को रोक देगा, क्योंकि रिटर्न स्टेटमेंट की अभिव्यक्ति अब "एक गैर-अस्थिर स्वचालित ऑब्जेक्ट का नाम" नहीं है (नहीं कि' temp' पहले ऐसा नाम था, लेकिन वैसे भी)। – Xeo

संबंधित मुद्दे