एनआरवीओ, आरवीओ और कॉपी एलिशन के साथ खेलें!
#include <iostream>
struct Verbose {
Verbose(Verbose const&){ std::cout << "copy ctor\n"; }
Verbose(Verbose &&){ std::cout << "move ctor\n"; }
Verbose& operator=(Verbose const&){ std::cout << "copy asgn\n"; }
Verbose& operator=(Verbose &&){ std::cout << "move asgn\n"; }
};
कि बहुत वर्बोज़ है:
यहाँ एक प्रकार है।
Verbose simple() { return {}; }
कि बहुत सरल है, और इसकी वापसी मान के प्रत्यक्ष निर्माण का उपयोग करता है:
यहाँ एक समारोह है। यदि Verbose
में कॉपी या मूवी कन्स्ट्रक्टर की कमी है, तो उपरोक्त फ़ंक्शन काम करेगा!
Verbose simple_RVO() { return Verbose(); }
यहाँ
अनाम Verbose()
अस्थायी वस्तु रिटर्न मान पर स्वयं की कॉपी करने के लिए कहा जा रहा है:
यहाँ एक समारोह का उपयोग करता है RVO है। आरवीओ का मतलब है कि संकलक उस प्रतिलिपि को छोड़ सकता है, और वापसी मूल्य में सीधे Verbose()
का निर्माण कर सकता है, अगर केवल एक प्रतिलिपि या कन्स्ट्रक्टर है तो केवल तभी। प्रतिलिपि या चालक कन्स्ट्रक्टर नहीं कहा जाता है, बल्कि इसके बजाय।
यहाँ एक समारोह NRVO का उपयोग करता है:
Verbose simple_NRVO() {
Verbose retval;
return retval;
}
NRVO होने के लिए, हर पथ ठीक उसी वस्तु लौटना चाहिए, और आप इसके बारे में डरपोक नहीं किया जा सकता है (यदि आप करने के लिए दिया गया मान डाली एक संदर्भ, फिर उस संदर्भ को वापस करें, जो एनआरवीओ को अवरुद्ध करेगा)। इस मामले में, कंपाइलर क्या करता है नामित ऑब्जेक्ट retval
सीधे वापसी मूल्य स्थान में बनाता है। आरवीओ के समान, एक प्रतिलिपि या चालक निर्माता मौजूद होना चाहिए, लेकिन इसे नहीं कहा जाता है। ,,
Verbose simple_no_NRVO(bool b) {
Verbose retval1;
Verbose retval2;
if (b)
return retval1;
else
return retval2;
}
इसे वापस कर सकता है के रूप में दो संभावित नामित वस्तुओं रहे हैं यह वापसी मान स्थान में उन दोनों का निर्माण नहीं कर सकते हैं तो यह कार्य करना होगा:
यहाँ एक समारोह है कि NRVO का उपयोग करने में विफल रहता है एक वास्तविक प्रति। सी ++ 11 में, प्रतिलिपि की गई वस्तु प्रतिलिपि के बजाय move
डी होगी, क्योंकि यह एक स्थानीय चर को एक साधारण रिटर्न स्टेटमेंट में फ़ंक्शन से वापस किया जा रहा है। तो कम से कम वह है।
Verbose v = simple(); // or simple_RVO, or simple_NRVO, or...
जब आप एक समारोह फोन है, आप इसे अपने तर्कों के साथ प्रदान करते हैं, और आप इसे सूचित जहां यह अपनी वापसी मान रखना चाहिए:
अंत में, दूसरे छोर पर प्रतिलिपि इलिजन है। कॉलर वापसी मूल्य को साफ करने और इसके लिए स्मृति (ढेर पर) आवंटित करने के लिए ज़िम्मेदार है।
यह संचार कॉलिंग सम्मेलन के माध्यम से किसी भी तरह से किया जाता है, अक्सर निहित रूप से (यानी, स्टैक पॉइंटर के माध्यम से)।
कई कॉलिंग सम्मेलनों के तहत, स्थान जहां वापसी मूल्य संग्रहीत किया जा सकता है, स्थानीय चर के रूप में उपयोग किया जा सकता है।
सामान्य में, अगर आप फार्म के एक चर राशि:
Verbose v = Verbose();
गर्भित प्रतिलिपि elided जा सकता है - Verbose()
बजाय एक अस्थायी किया जा रहा बनाया तो v
में कॉपी किया, v
में सीधे निर्माण किया है।इसी तरह, simple
(या simple_NRVO
, या जो कुछ भी) का वापसी मूल्य तब किया जा सकता है जब संकलक का रन टाइम मॉडल इसका समर्थन करता है (और यह आमतौर पर करता है)।
असल में, कॉलिंग साइट simple_*
को किसी विशेष स्थान पर वापसी मूल्य डालने के लिए बता सकती है, और बस उस स्थान को स्थानीय चर v
के रूप में मान सकती है।
ध्यान दें कि एनआरवीओ और आरवीओ और निहित कदम सभी फ़ंक्शन के भीतर किए गए हैं, और कॉलर को इसके बारे में कुछ भी पता नहीं है।
इसी तरह, कॉलिंग साइट पर eliding फ़ंक्शन के बाहर किया गया है, और यदि कॉलिंग सम्मेलन इसका समर्थन करता है तो आपको फ़ंक्शन के शरीर से किसी भी समर्थन की आवश्यकता नहीं है।
यह हर कॉलिंग सम्मेलन और रन टाइम मॉडल में सत्य नहीं होना चाहिए, इसलिए सी ++ मानक इन अनुकूलन को वैकल्पिक बनाता है।
प्रत्येक कंपाइलर इसे किसी भी तरह से करने के लिए चुन सकता है, और यदि आपके द्वारा वर्णित मामले में उनके लिए कोई समस्या है, तो संभवतः वे हमेशा आरवीओ का उपयोग करना चुनेंगे। जब आप घुसपैठ कर रहे हैं कि कैसे विभिन्न कंपेलर हुड के नीचे करते हैं, तो मेरा सुझाव है कि आप जेनरेट किए गए असेंबलर कोड को पढ़ लें। क्लैंग/जीसीसी जेनरेट असेंबली तक आसान पहुंच के लिए जीसीसी एक्सप्लोरर का उपयोग करें। – PlasmaHH
@PlasmaHH लेकिन आरवीओ हमेशा संभव नहीं है। –
इसका मतलब यह नहीं है कि जब कॉलिंग सम्मेलन में होता है या इसका उपयोग नहीं किया जाता है। कुछ असेंबलर को देखें कि वे इसे कैसे करते हैं। – PlasmaHH