दिलचस्प पोस्ट
जावा में दी गई विधि को कॉल करने वाले सभी तरीकों को मैं कैसे प्राप्त करूं? printf विसंगति के बाद "कांटा ()" UIWebView में कैश सामग्री को बाद में बाद में तेजी से लोड करने के लिए कैसे करें? मैं मौजूदा निष्पादन कोड के लिए HMODULE कैसे प्राप्त करूं? "एसटीएल" और "सी ++ स्टैंडर्ड लाइब्रेरी" के बीच अंतर क्या है? प्रतिबंधात्मक सामग्री सुरक्षा नीति के साथ पृष्ठ में आइफ्रेम इंजेक्शन मोंगोज़ज़.जेएस क्वेरी बनाना सिंक्रोनस रूप से चल रहा है एक खिड़की सेवा शुरू करें और सीएमडी लॉन्च करें ईएफ कोर के नवीनतम रात के निर्माण के साथ कई रिश्ते बनाने के लिए कैसे? वेबपेज। एस्पक्स से सर्वर-साइड पर जावास्क्रिप्ट द्वारा उत्पन्न स्क्रैप डेटा UIView पर कस्टम राउंडिंग कोने आईओएस: एक सबव्यू के लिए ऑटोरेटेशन को अक्षम करें Laravel 5.1 में जीमेल का उपयोग कर मेल कैसे भेजना है? जावास्क्रिप्ट में मैं एक नेमस्पेस कैसे घोषित करूं? समूह द्वारा अनन्य मूल्यों की संख्या को आर डेटा.फ्रेम में कैसे जोड़ें

जब std :: चालन फ़ंक्शन रिटर्न मान पर किया जाना चाहिए?

इस मामले में

struct Foo {}; Foo meh() { return std::move(Foo()); } 

मुझे पूरा यकीन है कि यह कदम अनावश्यक है, क्योंकि नव निर्मित Foo एक एक्सवेल्यू होगा।

लेकिन इन मामलों में क्या?

 struct Foo {}; Foo meh() { Foo foo; //do something, but knowing that foo can safely be disposed of //but does the compiler necessarily know it? //we may have references/pointers to foo. how could the compiler know? return std::move(foo); //so here the move is needed, right? } 

वहाँ कदम की जरूरत है, मुझे लगता है?

वेब के समाधान से एकत्रित समाधान "जब std :: चालन फ़ंक्शन रिटर्न मान पर किया जाना चाहिए?"

return std::move(foo); के मामले में return std::move(foo); यह move 12.8 / 32 की वजह से अतिरेक है:

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

return foo; एनआरवीओ का मामला है, इसलिए एलीजन की कॉपी की अनुमति है। foo एक लावल्यू है इसलिए कन्स्ट्रक्टर को "कॉपी" के लिए चयन किया गया है जिसे foo से foo के रिटर्न वैल्यू के लिए चुना गया है, यदि कोई मौजूद है तो meh निर्माता होना आवश्यक है।

move को जोड़ना एक संभावित प्रभाव पड़ता है, यद्यपि: यह चाल को लुप्त होने से रोकता है, क्योंकि return std::move(foo); एनआरवीओ के लिए योग्य नहीं है

जहां तक ​​मुझे पता है, 12.8 / 32 केवल उन परिस्थितियों को निर्धारित करता है जिसके तहत एक लावलू की प्रतिलिपि एक कदम से बदल सकती है। संकलक को सामान्य रूप से यह पता लगाने की अनुमति नहीं है कि प्रतिलिपि (डीएफए, का उपयोग करके) के बाद एक लावलू उपयोग नहीं किया गया है, और अपनी स्वयं की पहल पर बदलाव करना है। मैं यहाँ मान रहा हूँ कि दोनों के बीच एक अवलोकनत्मक अंतर है – यदि अवलोकन व्यवहार समान है तो "जैसा-अगर" नियम लागू होता है

इसलिए, शीर्षक में प्रश्न का उत्तर देने के लिए, वापसी मूल्य पर std::move उपयोग करें जब आप इसे स्थानांतरित करना चाहते हैं और यह वैसे भी स्थानांतरित नहीं होता। अर्थात्:

  • आप इसे स्थानांतरित करने के लिए चाहते हैं, और
  • यह एक लावलु है, और
  • यह कॉपी एलिज़न के लिए योग्य नहीं है, और
  • यह एक बाय-वैल्यू फ़ंक्शन पैरामीटर का नाम नहीं है।

यह ध्यान देने योग्य है कि यह काफी सुधारात्मक है और चालें आमतौर पर सस्ती हैं, आप शायद यह कहना चाहें कि गैर-टेम्पलेट कोड में आप इसे थोड़ा सरल कर सकते हैं। std::move उपयोग करें जब:

  • आप इसे स्थानांतरित करने के लिए चाहते हैं, और
  • यह एक लावलु है, और
  • आप इसके बारे में चिंतित नहीं हो सकते।

सरल नियमों का पालन करके आप कुछ कदम elision बलिदान जैसे std::vector जैसे प्रकार के लिए, जो आपको ले जाने के लिए सस्ता हैं, शायद आप कभी भी नोटिस नहीं करेंगे (और अगर आप नोटिस करते हैं कि आप अनुकूलित कर सकते हैं)। ऐसे std::array जैसे प्रकार के लिए ले जाने के लिए महंगा है, या टेम्पलेट्स के लिए जहां आपको पता नहीं है कि चालें सस्ते हैं या नहीं, तो आप इसके बारे में चिंतित होने की अधिक संभावना है।

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

रिटर्न वैल्यू पर, यदि वापसी अभिव्यक्ति सीधे एक स्थानीय लावलू (यानी एक बिंदु पर xvalue) के नाम से संदर्भित होती है तो std::move की कोई आवश्यकता नहीं है दूसरी तरफ, यदि वापसी अभिव्यक्ति पहचानकर्ता नहीं है, तो यह स्वचालित रूप से नहीं चलेगा, इसलिए उदाहरण के लिए, आपको इस मामले में स्पष्ट std::move आवश्यकता होगी:

 T foo(bool which) { T a = ..., b = ...; return std::move(which? a : b); // alternatively: return which? std::move(a), std::move(b); } 

जब एक नामित स्थानीय चर या एक अस्थायी अभिव्यक्ति सीधे वापस आती है, तो आपको स्पष्ट std::move से बचना चाहिए संकलक को उन मामलों में स्वतः ही (और भविष्य में) चलना चाहिए , और std::move जोड़ना अन्य अनुकूलन को प्रभावित कर सकता है।

इस बारे में बहुत सारे जवाब दिए गए हैं कि इसे स्थानांतरित नहीं किया जाना चाहिए, लेकिन सवाल यह है कि "कब जाना चाहिए?"

यहां इसका एक उदाहरण है कि इसका उपयोग कब किया जाना चाहिए:

 std::vector<int> append(std::vector<int>&& v, int x) { v.push_back(x); return std::move(v); } 

यानी, जब आपके पास कोई फ़ंक्शन होता है जो एक रैवल्यू संदर्भ लेता है, तो उसे संशोधित करता है, और उसके बाद इसकी एक प्रति देता है अब, व्यवहार में, यह डिजाइन लगभग हमेशा बेहतर होता है:

 std::vector<int> append(std::vector<int> v, int x) { v.push_back(x); return v; } 

जो आपको गैर-रैवल्यू मापदंडों को भी लेने की अनुमति देता है

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

एक सी + + संकलक std::move(foo) का उपयोग करने के लिए स्वतंत्र है:

  • अगर यह ज्ञात है कि foo अपने जीवनकाल के अंत में है, और
  • सी + + विनिर्देश द्वारा अनुमत सिमेंटिक प्रभावों के अलावा सी + + कोड के शब्दों पर कोई प्रभाव नहीं होगा।

यह C ++ कंपाइलर की अनुकूलन क्षमताओं पर निर्भर करता है, चाहे वह कौन सी परिवर्तनों को f(foo); foo.~Foo(); से गणना कर सकता है f(foo); foo.~Foo(); f(foo); foo.~Foo(); to f(std::move(foo)); foo.~Foo(); f(std::move(foo)); foo.~Foo(); प्रदर्शन के संदर्भ में या स्मृति खपत के मामले में लाभदायक होते हैं, जबकि सी ++ विनिर्देशन नियमों का पालन करते हुए


संकल्पनात्मक रूप से बोलते हुए वर्ष 2017 सी ++ कंपाइलर, जैसे जीसीसी 6.3.0, इस कोड को अनुकूलित करने में सक्षम हैं:

 Foo meh() { Foo foo(args); foo.method(xyz); bar(); return foo; } 

इस कोड में:

 void meh(Foo *retval) { new (retval) Foo(arg); retval->method(xyz); bar(); } 

जो प्रति कन्स्ट्रक्टर और Foo के नाशक को बुलाती है।


वर्ष 2017 सी ++ कंपाइलर, जैसे जीसीसी 6.3.0, इन कोड को अनुकूलित करने में असमर्थ हैं:

 Foo meh_value() { Foo foo(args); Foo retval(foo); return retval; } Foo meh_pointer() { Foo *foo = get_foo(); Foo retval(*foo); delete foo; return retval; } 

इन कोडों में

 Foo meh_value() { Foo foo(args); Foo retval(std::move(foo)); return retval; } Foo meh_pointer() { Foo *foo = get_foo(); Foo retval(std::move(*foo)); delete foo; return retval; } 

जिसका अर्थ है कि वर्ष 2017 प्रोग्रामर को ऐसे अनुकूलन को स्पष्ट रूप से निर्दिष्ट करना होगा।

std::move एक फ़ंक्शन से लौटने के दौरान पूरी तरह से अनावश्यक है, और वास्तव में आप के दायरे में आ जाता है – प्रोग्रामर – उन चीजों को निभाने की कोशिश कर रहा है जिन्हें आपको कंपाइलर पर छोड़ना चाहिए

क्या होता है जब आप std::move किसी फ़ंक्शन के बाहर कुछ std::move जो उस फ़ंक्शन में एक वैरिएबल स्थानीय नहीं है? आप कह सकते हैं कि आप कभी भी ऐसा कोड नहीं लिखेंगे, लेकिन यदि आप उस कोड को लिखते हैं जो सिर्फ ठीक है, और फिर इसे पुन: लागू करें और अनुपस्थित-दिमाग से std::move नहीं बदलते आप नीचे बग को मज़ेदार ट्रैक करेंगे

दूसरी तरफ, कंपाइलर, इन प्रकार की गलतियों को बनाने में असमर्थ है।

इसके अलावा: यह ध्यान देने योग्य है कि किसी फ़ंक्शन से स्थानीय वैरिएबल लौटने के लिए जरूरी नहीं कि किसी रैवल्यू को बनाने या स्थानांतरित करें शब्दों का उपयोग करें।

यहाँ देखें।