जैसा कि @ JDługosz टिप्पणियों में बताते हैं, हर्ब दूसरे में (बाद में?) बात करने की सलाह देता है, यहाँ से मोटे तौर पर देखें: https://youtu.be/xnqTKD8uD64?t=54m50s ।
उनकी सलाह केवल एक फ़ंक्शन के लिए मान मापदंडों का उपयोग करके उबलती है f
जो तथाकथित सिंक तर्क लेती है, यह मानते हुए कि आप इन सिंक नियुक्तियों से निर्माण करेंगे।
यह सामान्य दृष्टिकोण f
क्रमशः लैवल्यू और रिवेल्यू तर्कों के अनुरूप कार्यान्वयन की तुलना में केवल लेवल्यू और रिवेल्यू तर्क दोनों के लिए एक कदम निर्माणकर्ता के ओवरहेड को जोड़ता है। यह देखने के लिए कि ऐसा क्यों है, मान लीजिए कि f
एक मान पैरामीटर है, जहां T
कुछ प्रतिलिपि और रचनात्मक प्रकार स्थानांतरित होते हैं:
void f(T x) {
T y{std::move(x)};
}
f
एक लैवल्यू तर्क के साथ कॉल करने के परिणामस्वरूप एक कॉपी कंस्ट्रक्टर को निर्माण के लिए बुलाया जाएगा x
, और एक निर्माण कंस्ट्रक्टर को निर्माण के लिए बुलाया जाएगा y
। दूसरी ओर, f
एक प्रतिद्वंद्विता के तर्क के साथ कॉल करने से एक निर्माण करने वाले को निर्माण करने के लिए बुलाया जाएगा x
, और एक अन्य चाल निर्माणकर्ता को निर्माण के लिए बुलाया जाएगा y
।
सामान्य तौर पर, f
lvalue तर्कों के लिए इष्टतम कार्यान्वयन निम्नानुसार है:
void f(const T& x) {
T y{x};
}
इस मामले में, केवल एक प्रतिलिपि निर्माता को निर्माण करने के लिए कहा जाता है y
। f
आम तौर पर फिर से, तर्क तर्कों के लिए इष्टतम कार्यान्वयन है:
void f(T&& x) {
T y{std::move(x)};
}
इस मामले में, केवल एक चाल निर्माणकर्ता को निर्माण करने के लिए कहा जाता है y
।
इसलिए एक समझदार समझौता एक मूल्य पैरामीटर लेना है और इष्टतम कार्यान्वयन के संबंध में या तो अंतराल या तर्क तर्कों के लिए एक अतिरिक्त चाल निर्माता कॉल है, जो हर्ब की बात में दी गई सलाह भी है।
जैसा कि @ JDługosz ने टिप्पणियों में बताया है, मूल्य से गुजरना केवल उन कार्यों के लिए समझ में आता है जो सिंक तर्क से कुछ ऑब्जेक्ट का निर्माण करेंगे। जब आपके पास एक फ़ंक्शन होता है f
जो इसके तर्क की प्रतिलिपि बनाता है, तो पास-दर-मूल्य दृष्टिकोण सामान्य पास-बाय-कॉन्स्ट-रेफ़रेंस दृष्टिकोण की तुलना में अधिक ओवरहेड होगा। किसी फ़ंक्शन के लिए पास-दर-मूल्य दृष्टिकोण f
जो उसके पैरामीटर की एक प्रति को बरकरार रखता है, उसके पास फ़ॉर्म होगा:
void f(T x) {
T y{...};
...
y = std::move(x);
}
इस मामले में, एक प्रतिवाद तर्क के लिए एक प्रतिलिपि निर्माण और एक चाल असाइनमेंट है, और एक चाल निर्माण और एक तर्क तर्क के लिए चाल असाइनमेंट है। एक तर्क तर्क के लिए सबसे इष्टतम मामला है:
void f(const T& x) {
T y{...};
...
y = x;
}
यह केवल एक असाइनमेंट के लिए उबालता है, जो पास-पास-मूल्य दृष्टिकोण के लिए आवश्यक कॉपी कंस्ट्रक्टर प्लस मूव असाइनमेंट की तुलना में बहुत सस्ता है। इसका कारण यह है कि असाइनमेंट मौजूदा आवंटित मेमोरी का पुन: उपयोग कर सकता हैy
, और इसलिए (डी) आवंटन को रोक सकता है, जबकि कॉपी कंस्ट्रक्टर आमतौर पर मेमोरी आवंटित करेगा।
एक तर्क तर्क के लिए f
कि प्रतिलिपि बनाए रखने के लिए सबसे इष्टतम कार्यान्वयन का रूप है:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
तो, इस मामले में केवल एक चाल असाइनमेंट। उस संस्करण के लिए एक अंतराल पारित करना एक कॉन्स्टिट्यूशन f
लेता है केवल एक चाल असाइनमेंट के बजाय एक असाइनमेंट खर्च होता है। इतना अपेक्षाकृत, का संस्करणf
सामान्य बात है कि सामान्य कार्यान्वयन के रूप में इस मामले में एक कास्ट संदर्भ लेने बेहतर है।
तो सामान्य तौर पर, सबसे इष्टतम कार्यान्वयन के लिए, आपको ओवरलोड करने या किसी प्रकार की सही अग्रेषण करने की आवश्यकता होगी जैसा कि बात में दिखाया गया है। दोष आपके लिए आवश्यक अधिभार की संख्या में एक दहनशील विस्फोट है, इस बात के लिए मापदंडों की संख्या के आधार पर कि f
आप तर्क के मूल्य वर्ग पर अधिभार का विकल्प चुनते हैं। परफेक्ट फ़ॉरवर्डिंग में एक खामी है जो f
एक टेम्प्लेट फ़ंक्शन बन जाता है, जो इसे आभासी बनाने से रोकता है, और यदि आप इसे 100% सही प्राप्त करना चाहते हैं, तो (अधिक विवरण कोड देखें)