गारंटीड कॉपी एलिसन कैसे काम करता है?


89

2016 ओलु आईएसओ आईएसओ सी + + मानकों की बैठक में, सरलीकृत मूल्य श्रेणियों के माध्यम से गारंटीड कॉपी एलिसन नामक एक प्रस्ताव को मानक समिति द्वारा सी ++ 17 में वोट दिया गया था।

वास्तव में गारंटीकृत कॉपी एलिसन कैसे काम करता है? क्या यह कुछ मामलों को कवर करता है जहां कॉपी एलिजन की अनुमति पहले से ही थी, या क्या कॉपी एलीजन की गारंटी के लिए कोड परिवर्तन आवश्यक हैं?

जवाबों:


129

कई परिस्थितियों में प्रतिलिपि बनाने की अनुमति दी गई थी। हालाँकि, भले ही इसकी अनुमति दी गई थी, फिर भी कोड को काम करने में सक्षम होना चाहिए जैसे कि प्रतिलिपि के बिना। अर्थात्, एक सुलभ कॉपी और / या कंस्ट्रक्टर को स्थानांतरित करना था।

गारंटीकृत प्रतिलिपि एलिसन कई सी ++ अवधारणाओं को पुनर्परिभाषित करता है, जैसे कि कुछ निश्चित परिस्थितियां जहां प्रतियां / चाल को खत्म किया जा सकता है, वास्तव में कॉपी / चाल को उकसाता नहीं है । संकलक एक प्रतिलिपि eliding नहीं है; मानक कहता है कि ऐसी कोई भी नकल कभी नहीं हो सकती है।

इस समारोह पर विचार करें:

T Func() {return T();}

गैर-गारंटीकृत प्रतिलिपि एलिसन नियमों के तहत, यह एक अस्थायी निर्माण करेगा, फिर उस अस्थायी से फ़ंक्शन के रिटर्न वैल्यू में स्थानांतरित होगा। उस चाल कार्रवाई को खत्म किया जा सकता है, लेकिन फिर Tभी एक सुलभ चाल निर्माणकर्ता होना चाहिए , भले ही इसका उपयोग कभी न किया जाए।

इसी तरह:

T t = Func();

यह की प्रतिलिपि आरंभीकरण है t। यह tरिटर्न वैल्यू के साथ इनिशियलाइज़ कॉपी करेगा Func। हालांकि, Tअभी भी एक चाल निर्माणकर्ता होना चाहिए, भले ही इसे बुलाया नहीं जाएगा।

गारंटीकृत प्रतिलिपि एलिसन एक प्रचलित अभिव्यक्ति के अर्थ को फिर से परिभाषित करता है । प्री-सी ++ 17, प्रचलित अस्थायी वस्तुएं हैं। C ++ में 17, एक prvalue अभिव्यक्ति केवल कुछ जो कर सकते हैं अमल में लाना एक अस्थायी है, लेकिन यह अभी तक एक अस्थायी नहीं है।

यदि आप प्रील्यू के प्रकार के ऑब्जेक्ट को इनिशियलाइज़ करने के लिए किसी प्रचलन का उपयोग करते हैं, तो कोई भी अस्थायी नहीं होता है। जब आप ऐसा करते हैं return T();, तो यह प्रील्यूव्यू के जरिए फंक्शन के रिटर्न वैल्यू को इनिशियलाइज़ करता है। चूंकि वह फ़ंक्शन लौटता है T, कोई अस्थायी नहीं बनाया जाता है; प्रील्यू के आरंभ को सीधे रिटर्न वैल्यू में सम्मिलित किया जाता है।

समझने वाली बात यह है कि, चूंकि रिटर्न वैल्यू एक प्रचलन है, यह अभी तक कोई वस्तु नहीं है। यह किसी वस्तु के लिए केवल एक इनिशियलाइज़र है, जैसे T()है।

जब आप करते हैं T t = Func();, तो रिटर्न वैल्यू का मूल्य सीधे ऑब्जेक्ट को इनिशियलाइज़ करता है t; कोई "अस्थायी और प्रतिलिपि / चाल बनाएँ" चरण नहीं है। चूँकि Func()'s रिटर्न वैल्यू' के बराबर एक प्रील्यूव है T(), इसलिए tइसे सीधे इनिशियलाइज़ किया जाता है T(), जैसे कि आपने किया था T t = T()

अगर किसी प्रचलन का उपयोग किसी अन्य तरीके से किया जाता है, तो प्रचलन एक अस्थायी वस्तु को उत्प्रेरित करेगा, जिसका उपयोग उस एक्सप्रेशन में किया जाएगा (या अभिव्यक्ति नहीं होने पर उसे छोड़ दिया जाएगा)। तो अगर आपने किया const T &rt = Func();, तो प्रचलन एक अस्थायी (शुरुआती के T()रूप में उपयोग करते हुए ) होगा, जिसका संदर्भ rtसामान्य अस्थायी जीवनकाल एक्सटेंशन सामान के साथ जमा किया जाएगा ।

एक चीज़ की गारंटी की अनुमति आपको ऐसा करने की अनुमति देती है जो ऐसी वस्तुएं हैं जो मोबाइल हैं। उदाहरण के लिए, lock_guardइसे कॉपी या स्थानांतरित नहीं किया जा सकता है, इसलिए आपके पास ऐसा कोई फ़ंक्शन नहीं हो सकता है जो इसे मूल्य से लौटाए। लेकिन गारंटीकृत प्रतिलिपि के साथ, आप कर सकते हैं।

गारंटीकृत एलिसन भी प्रत्यक्ष आरंभीकरण के साथ काम करता है:

new T(FactoryFunction());

यदि मूल्य से FactoryFunctionरिटर्न मिलता Tहै, तो यह अभिव्यक्ति आवंटित मेमोरी में रिटर्न वैल्यू की नकल नहीं करेगी। यह बजाय स्मृति को आबंटित और प्रयोग करेंगे आबंटित स्मृति समारोह कॉल सीधे के लिए वापसी मान स्मृति के रूप में।

इसलिए फैक्ट्री फ़ंक्शंस जो वैल्यू के हिसाब से लौटते हैं, ढेर की आवंटित मेमोरी को सीधे बिना इसके बारे में जाने बिना भी इनिशियलाइज़ कर सकते हैं। इसलिए जब तक ये फ़ंक्शन आंतरिक रूप से गारंटीकृत कॉपी एलिसन के नियमों का पालन करते हैं, तब तक। उन्हें प्रकार के एक प्रचलन को वापस करना होगा T

बेशक, यह भी काम करता है:

new auto(FactoryFunction());

मामले में आप टाइपनेम लिखना पसंद नहीं करते हैं।


यह पहचानना महत्वपूर्ण है कि उपरोक्त गारंटियाँ केवल प्रचलन के लिए काम करती हैं। अर्थात, नामांकित चर वापस करते समय आपको कोई गारंटी नहीं मिलती है :

T Func()
{
   T t = ...;
   ...
   return t;
}

इस उदाहरण में, tअभी भी एक सुलभ कॉपी / मूव कंस्ट्रक्टर होना चाहिए। हां, कंपाइलर कॉपी / मूव को ऑप्टिमाइज़ करना चुन सकता है। लेकिन कंपाइलर को अभी भी एक सुलभ कॉपी / मूव कंस्ट्रक्टर के अस्तित्व को सत्यापित करना चाहिए।

तो नाम वापसी मूल्य अनुकूलन (NRVO) के लिए कुछ भी नहीं बदलता है।


1
@BenVoigt: गैर-तुच्छ रूप से कॉपी किए जाने वाले उपयोगकर्ता-परिभाषित प्रकारों को रजिस्टरों में रखना एक व्यवहार्य बात नहीं है जो एबीआई कर सकता है, चाहे वह उपलब्ध हो या न हो।
निकोल बोलस

1
अब जब नियम सार्वजनिक हो गए हैं, तो "प्रिव्यूल्स इनिशियलाइज़ेशन" अवधारणा के साथ इसे अपडेट करने लायक हो सकता है।
जोहान्स शहाब -

6
@ जोहान्सचैब-लिट: यह केवल "अस्पष्ट" है यदि आप पूरी तरह से C ++ मानक के माइनुटिया के बारे में बहुत अधिक जानते हैं। C ++ समुदाय के 99% के लिए, हम जानते हैं कि "गारंटीकृत प्रतिलिपि एलिसन" क्या है। फीचर का प्रस्ताव करने वाले वास्तविक पेपर का शीर्षक "गारंटीड कॉपी एलीशन" भी है। "सरलीकृत मूल्य श्रेणियों के माध्यम से" जोड़ना केवल उपयोगकर्ताओं को समझने के लिए भ्रमित और कठिन बनाता है। यह भी एक मिथ्या नाम है, क्योंकि ये नियम वास्तव में मूल्य श्रेणियों के आसपास के नियमों को "सरल" नहीं करते हैं। आप इसे पसंद करते हैं या नहीं, "गारंटीकृत कॉपी एलिसन" शब्द इस सुविधा को संदर्भित करता है और कुछ नहीं।
निकोल बोलस

1
मैं ऐसा करने के लिए सक्षम होना चाहते हैं और इसे ले जाने में सक्षम है। मुझे लगता है कि यह वास्तव में सिर्फ एक (एक शॉट) std::function<T()>है।
यक्क - एडम नेवरुमोंट

1
@LukasSalich: यह एक C ++ 11 प्रश्न है। यह उत्तर C ++ 17 फीचर के बारे में है।
निकोल बोलस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.