2010-04-26 20 views
6

(मैं -O2 साथ जीसीसी उपयोग कर रहा हूँ।)कॉपी कन्स्ट्रक्टर यहां क्यों नहीं है?

यह प्रतिलिपि निर्माता छिपाना करने के लिए एक सीधा अवसर की तरह लगता है के बाद से वहाँ एक की एक bar की कॉपी में एक फ़ील्ड का मान तक पहुँचने के लिए कोई दुष्प्रभाव हैं foo; लेकिन कॉपी कन्स्ट्रक्टर कहा जाता है, क्योंकि मुझे आउटपुट meep meep! मिलता है।

#include <iostream> 

struct foo { 
    foo(): a(5) { } 
    foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; } 
    int a; 
}; 

struct bar { 
    foo F() const { return f; } 
    foo f; 
}; 

int main() 
{ 
    bar b; 
    int a = b.F().a; 
    return 0; 
} 
+0

क्या आप पूछ रहे हैं कि कॉपी इंस्टालर को मूल उदाहरण के फील्ड मान को वापस करने के बजाय क्यों कहा जाता है? –

+1

आप कैसे जानते हैं कि यह elided नहीं था। और आपको ऐसा क्यों लगता है कि यह होना चाहिए था? कृपया इन प्रश्नों के संबंध में अपने प्रश्न को संशोधित करें। –

+0

@ माइकल, यह सही है। –

उत्तर

11

यह है 12.8/15 में वर्णित प्रतिलिपि ctor इलिजन के दो कानूनी मामलों की न:

वापसी मान अनुकूलन (जहां एक स्वत: चर एक समारोह से वापस आ रहा है, और रिटर्न मान पर कि स्वत: की नकल सीधे वापसी मूल्य में स्वचालित निर्माण करके elided है) - नहीं। f एक स्वचालित चर नहीं है।

अस्थायी प्रारंभकर्ता (जहां किसी अस्थायी प्रारंभिक वस्तु को कॉपी किया गया है, और अस्थायी बनाने और इसे कॉपी करने के बजाय, अस्थायी मान सीधे गंतव्य में बनाया गया है) - नोप f कोई अस्थायी नहीं है। b.F() एक अस्थायी है, लेकिन इसे कहीं भी कॉपी नहीं किया गया है, इसमें केवल डेटा सदस्य का उपयोग किया गया है, इसलिए जब तक आप F() से बाहर निकलते हैं, वहां कुछ भी नहीं है।

कॉपी सीटीआर एलिजन सेब के कानूनी मामलों में से कोई भी नहीं, और f की प्रतिलिपि F() के वापसी मूल्य पर प्रोग्राम के अवलोकन योग्य व्यवहार को प्रभावित करती है, मानक इसे समाप्त करने से मना करता है। यदि आप कुछ गैर-देखने योग्य गतिविधि के साथ प्रिंटिंग को बदल चुके हैं, और असेंबली की जांच की है, तो आप देख सकते हैं कि इस प्रतिलिपि निर्माता को अनुकूलित किया गया है। लेकिन यह "as-if" नियम के तहत होगा, कॉपी कन्स्ट्रक्टर एलिजन नियम के तहत नहीं।

+1

दिलचस्प, धन्यवाद। मानक इस मामले की अनुमति क्यों नहीं देता है, यद्यपि? –

+0

इसी कारण से कि यह किसी भी पुराने फ़ंक्शन को छोड़ने की अनुमति नहीं देता है, जिसमें 'std :: cout <<' के लिए एक कॉल शामिल है। बड़ा सवाल यह है कि, मानक * कभी * "अनुकूलन" क्यों अनुमति देता है जो प्रोग्राम के देखने योग्य व्यवहार को बदलता है? जवाब यह है कि इसे अस्थायी की प्रतिलिपि बनाने और मूल्य वापसी मूल्यों की हास्यास्पद श्रृंखलाओं से बचने के लिए आवश्यक समझा जाता था, और कोई भी उन दो मामलों में प्रदर्शन की प्रतिलिपि पर भरोसा नहीं करना चाहता था। यदि आप अपने मामले में प्रतिलिपि से बचना चाहते हैं तो आप 'कॉन्स फू' और 'वापस कर सकते हैं। प्रतिलिपि ctor elision के दो कानूनी मामलों में, आप इस तरह एक प्रतिलिपि से बच नहीं सकते हैं। –

+0

इसलिए मुझे संदेह है कि आपके उदाहरण को कवर करने के लिए elision जोड़ा गया था, यह है कि आप इसे स्वयं कर सकते हैं, और आवश्यकता की अनुपस्थिति में, व्यवहार-तोड़ने वाले विशेष मामले खराब हैं। हालांकि, यह सिर्फ एक अनुमान है। –

1

प्रतिलिपि निर्माता कहा जाता है क) कोई गारंटी नहीं आप संशोधन के बिना फ़ील्ड मान कॉपी कर रहे हैं है, और ख) क्योंकि आपके प्रति निर्माता एक पक्ष प्रभाव है (संदेश प्रिंट), क्योंकि है।

+3

साइड इफेक्ट्स के साथ रचनाकारों की प्रतिलिपि बनाई जा सकती है। नीचे की रेखा - इस तरह की प्रति रचनाकारों को मत लिखो। –

1

कॉपी एलिजन के बारे में सोचने का एक बेहतर तरीका अस्थायी वस्तु के संदर्भ में है। इस तरह मानक इसका वर्णन करता है। एक अस्थायी को स्थायी वस्तु में "तब्दील" होने की अनुमति है यदि इसे अपने विनाश से तुरंत स्थायी वस्तु में कॉपी किया गया हो।

यहां आप फ़ंक्शन रिटर्न में एक अस्थायी ऑब्जेक्ट बनाते हैं। यह वास्तव में किसी भी चीज़ में भाग नहीं लेता है, इसलिए आप इसे छोड़ना चाहते हैं। लेकिन क्या आप

b.F().a = 5; 

यदि प्रतिलिपि elided थे किया था, और आप मूल वस्तु पर संचालित है, आप एक गैर संदर्भ के माध्यम से b संशोधित होता।

+0

लेकिन संकलक केवल प्रतिलिपि को तब्दील कर सकता है अगर इसे एक लाभा के रूप में उपयोग नहीं किया जाता है। –

+0

@ जेसे: मेरे उदाहरण में यह एक लाभा के रूप में उपयोग नहीं किया जाता है। इसका उपयोग 'ऑपरेटर' के बाएं हाथ के रूप में किया जाता है। – Potatoswatter

+0

@Potatoswatter - लेकिन फिर '.' ऑपरेटर का परिणाम एक लाभा के रूप में प्रयोग किया जाता है। मैं अपने सी ++ शब्दावली के बारे में 100% निश्चित नहीं हूं, इसलिए शायद lvalue सही शब्द नहीं है, लेकिन जिस अवधारणा को मैं ढूंढ रहा हूं वह संक्रमणीय होना चाहिए। –

2

कॉपी एलिजन केवल तब होता है जब एक प्रति वास्तव में आवश्यक नहीं होती है। विशेष रूप से, यह तब होता है जब एक ऑब्जेक्ट होता है (इसे ए कॉल करें) जो किसी फ़ंक्शन के निष्पादन की अवधि के लिए मौजूद होता है, और दूसरी ऑब्जेक्ट (इसे बी कहते हैं) जिसे पहले ऑब्जेक्ट से बनाया गया प्रतिलिपि बनाई जाएगी, और इसके बाद तत्काल , ए नष्ट हो जाएगा (यानी समारोह से बाहर निकलने पर)।

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

आपका कोड उस पैटर्न को फिट नहीं करता है - पहली वस्तु दूसरे ऑब्जेक्ट को प्रारंभ करने के लिए उपयोग किए जाने के तुरंत बाद मौजूद नहीं रहती है। F() रिटर्न के बाद, ऑब्जेक्ट के दो उदाहरण हैं। यह मामला है, [नामित] रिटर्न वैल्यू ऑप्टिमाइज़ेशन (उर्फ कॉपी एलिशन) बस लागू नहीं होता है।

डेमो कोड जब प्रतिलिपि इलिजन लागू होगा:

#include <iostream> 

struct foo { 
    foo(): a(5) { } 
    foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; } 
    int a; 
}; 

int F() { 
    // RVO 
    std::cout << "F\n"; 
    return foo(); 
} 

int G() { 
    // NRVO 
    std::cout << "G\n"; 
    foo x; 
    return x; 
} 

int main() { 
    foo a = F(); 
    foo b = G(); 
    return 0; 
} 

दोनों एमएस कुलपति ++ और जी ++ का अनुकूलन दूर दोनों ctors इस कोड से कॉपी अनुकूलन के साथ चालू कर दिया। जी ++ ऑप्टिमाइज़ेशन बंद होने पर भी दोनों को अनुकूलित करता है। ऑप्टिमाइज़ेशन बंद होने के साथ, वीसी ++ अज्ञात रिटर्न को ऑप्टिमाइज़ करता है, लेकिन नामित रिटर्न के लिए कॉपी सीटीआर का उपयोग करता है।

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

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