2013-10-09 16 views
35

कई मामलों में जब किसी फ़ंक्शन से स्थानीय लौटाते हैं, तो आरवीओ अंदर आ जाता है। हालांकि, मैंने सोचा कि स्पष्ट रूप से std::move का उपयोग करके आरवीओ नहीं होने पर कम से कम लागू हो जाएगा, लेकिन जब भी संभव हो तो आरवीओ लागू होता है। हालांकि, ऐसा लगता है कि यह मामला नहीं है।std :: move आरवीओ को क्यों रोकता है?

#include "iostream" 

class HeavyWeight 
{ 
public: 
    HeavyWeight() 
    { 
     std::cout << "ctor" << std::endl; 
    } 

    HeavyWeight(const HeavyWeight& other) 
    { 
     std::cout << "copy" << std::endl; 
    } 

    HeavyWeight(HeavyWeight&& other) 
    { 
     std::cout << "move" << std::endl; 
    } 
}; 

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return heavy; 
} 

int main() 
{ 
    auto heavy = MakeHeavy(); 
    return 0; 
} 

मैं कुलपति ++ 11 और जीसीसी 4.71, डिबग और रिलीज (-O2) config के साथ इस कोड का परीक्षण किया। प्रतिलिपि ctor कभी नहीं कहा जाता है। चाल ctor केवल डीबीयूजी विन्यास में वीसी ++ 11 द्वारा बुलाया जाता है। दरअसल, विशेष रूप से इन कंपाइलरों के साथ सब कुछ ठीक लगता है, लेकिन मेरे ज्ञान के लिए, आरवीओ वैकल्पिक है।

हालांकि, अगर मैं स्पष्ट रूप से move का उपयोग करें:

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return std::move(heavy); 
} 

कदम ctor हमेशा कहा जाता है। तो इसे "सुरक्षित" बनाने की कोशिश करना इससे भी बदतर हो जाता है।

मेरे प्रश्न हैं:
- std::move क्यों आरवीओ को रोकता है?
- "सर्वश्रेष्ठ के लिए आशा" और आरवीओ पर भरोसा करने के लिए बेहतर कब होता है, और मुझे स्पष्ट रूप से std::move का उपयोग कब करना चाहिए? या, दूसरे शब्दों में, मैं संकलक अनुकूलन को अपना काम कैसे कर सकता हूं और आरवीओ लागू नहीं होने पर अभी भी कदम लागू कर सकता हूं?

+4

लोग अभी भी "सर्वश्रेष्ठ के लिए आशा" के बारे में क्यों बात करते हैं? वे किस प्रकार का कंपाइलर उपयोग कर रहे हैं जिसमें सी ++ 11 समर्थन है लेकिन आरवीओ ठीक से नहीं हो सकता है? –

+1

प्रतिलिपि कॉपी करें (आरवीओ के पीछे तंत्र) केवल कुछ निश्चित, सख्त स्थितियों के तहत अनुमति है। 'Std :: move' लिखना उन शर्तों को पूरा होने से रोकता है। –

+2

@KerrekSB और std :: move द्वारा रोके गए इन स्थितियां हैं ...? – cdoubleplusgood

उत्तर

25

मामलों में जहां प्रति और ले जाने के इलिजन अनुमति दी है अनुभाग 12.8 §31 स्टैंडर्ड (संस्करण N3690) के में पाया जाता है:

जब कुछ मानदंडों को पूरा किया जाता है, एक कार्यान्वयन कॉपी/कदम को छोड़ करने की अनुमति है क्लास ऑब्जेक्ट का निर्माण, भले ही प्रतिलिपि/चाल ऑपरेशन के लिए चयनित कन्स्ट्रक्टर और/या ऑब्जेक्ट के विनाशक के दुष्प्रभाव हों। ऐसे मामलों में, कार्यान्वयन एक ही वस्तु का जिक्र करने के केवल दो अलग-अलग तरीकों के रूप में छोड़े गए प्रतिलिपि/चालन संचालन के स्रोत और लक्ष्य का इलाज करता है, और उस वस्तु का विनाश उस समय के बाद होता है जब दोनों वस्तुएं होतीं अनुकूलन के बिना नष्ट कर दिया। कॉपी/कदम आपरेशन के इस इलिजन, प्रतिलिपि इलिजन कहा जाता है, निम्नलिखित परिस्थितियों (जो कई प्रतियां खत्म करने के लिए जोड़ा जा सकता है) में की अनुमति दी है:

    एक वर्ग वापसी प्रकार के साथ एक समारोह में एक return बयान में
  • , जब अभिव्यक्ति एक गैर-अस्थिर स्वचालित ऑब्जेक्ट (फ़ंक्शन या कैच-क्लॉज पैरामीटर के अलावा) का नाम होता है, उसी सीवी-अयोग्य प्रकार के साथ फ़ंक्शन रिटर्न प्रकार के रूप में, प्रतिलिपि/चाल ऑपरेशन स्वचालित ऑब्जेक्ट का निर्माण करके छोड़ा जा सकता है सीधे फ़ंक्शन के रिटर्न वैल्यू
  • [...]
  • जब कोई अस्थायी क्लास ऑब्जेक्ट जिसमें कोई नहीं है एक संदर्भ के लिए बाध्य नहीं किया गया है (12.2) एक ही सीवी-अयोग्य प्रकार के साथ एक क्लास ऑब्जेक्ट में प्रतिलिपि/स्थानांतरित किया जाएगा, प्रतिलिपि/चाल अभियान को अस्थायी वस्तु को सीधे छोड़ी गई प्रतिलिपि/चाल
  • के लक्ष्य में छोड़कर छोड़ा जा सकता है
  • [...]

(दो मामलों मैं बाहर छोड़ दिया फेंकने और अपवाद वस्तुओं जो मैं अनुकूलन के लिए कम महत्वपूर्ण मानते हैं पकड़ने के मामले को देखें।)

इसलिए एक वापसी कथन प्रतिलिपि इलिजन में केवल हो सकता है, यदि अभिव्यक्ति स्थानीय चर का नाम है। यदि आप std::move(var) लिखते हैं, तो यह अब एक चर का नाम नहीं है। इसलिए यदि संकलक मानक के अनुरूप होना चाहिए तो संकलक इस कदम को दूर नहीं कर सकता है।

स्टीफन टी। Lavavej इस बारे में Going Native 2013 पर बात की और वास्तव में अपनी स्थिति समझाया और क्यों std::move() से बचने के लिए। 38:04 मिनट को देखना शुरू करें। असल में, रिटर्न प्रकार के स्थानीय चर को लौटने पर, इसे आमतौर पर एक रैवल्यू के रूप में माना जाता है जिससे डिफ़ॉल्ट रूप से स्थानांतरित होता है।

+0

यह वह उत्तर है जिसके लिए मैं उम्मीद कर रहा था। मैं स्थिति से खुश नहीं हूं, लेकिन मैं बेहतर समझता हूं। – cdoubleplusgood

+5

हमें शायद ठीक करना चाहिए ताकि 'वापसी std :: move' को elided किया जा सके। अगर हम सी ++ को बता सकते हैं कि फ़ंक्शन का संदर्भ वापसी मान फ़ंक्शन के एक विशेष संदर्भ इनपुट मान के समान होने की गारंटी है, तो कुछ रोचक परिणाम खुल सकते हैं। (गैर-तुच्छ अभिव्यक्तियों से एलिसन, एक अस्थायी इनपुट तर्क का आजीवन विस्तार अस्थायी लौटाए बिना एक समारोह में, दो के लिए: दूसरा दूसरा आईएमएचओ अधिक महत्वपूर्ण है)। – Yakk

+0

हां, मैं इस बात को समझने में सक्षम नहीं होने के लिए तर्कसंगत समझ में नहीं आता हूं। कंपाइलर लेखकों को और अधिक समय देने की इजाजत देना मुश्किल है? – Adrian

13

मैं संकलक अनुकूलन को अपना काम कैसे कर सकता हूं और आरवीओ लागू नहीं होने पर अभी भी कदम लागू कर सकता हूं?

इस तरह:

HeavyWeight MakeHeavy() 
{ 
    HeavyWeight heavy; 
    return heavy; 
} 

एक चाल में वापसी को बदलने अनिवार्य है।

+0

तो स्थानीय लौटने पर सबसे खराब मामले में कदम उठाने की गारंटी है, और आरवीओ सर्वोत्तम मामले में लागू होता है? – cdoubleplusgood

+2

@cdoubleplusgood: हां। – Xeo

+0

मुझे लगता है कि मुझे 'वापसी std :: move'' के लिए कुछ grep'ping करने की आवश्यकता है;) – goji

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