2011-12-19 2 views
8

मैं सी ++ 11 में कुछ नई सुविधाओं के साथ खेल रहा हूं, और मैंने निम्नलिखित प्रोग्राम लिखने की कोशिश की, उम्मीद है कि यह काम न करे। मेरे आश्चर्य करने के लिए बहुत है, यह (जीसीसी 4.6.1 पर लिनक्स 86 पर 'एसटीडी = C++ 0x' ध्वज के साथ) करता है:सी ++ लैम्ब्डास, कैप्चरिंग, स्मार्ट पटर, और स्टैक: यह क्यों काम करता है?

#include <functional> 
#include <iostream> 
#include <memory> 

std::function<int()> count_up_in_2s(const int from) { 
    std::shared_ptr<int> from_ref(new int(from)); 
    return [from_ref]() { return *from_ref += 2; }; 
} 

int main() { 
    auto iter_1 = count_up_in_2s(5); 
    auto iter_2 = count_up_in_2s(10); 

    for (size_t i = 1; i <= 10; i++) 
     std::cout << iter_1() << '\t' << iter_2() << '\n' 
     ; 
} 

मैं उम्मीद कर रहा था 'from_ref' हटाए जाने के लिए जब से प्रत्येक निष्पादन लौम्बा रन लौटा यहां मेरा तर्क है: एक बार count_up_in_2s चलाया जाता है, तो_ref को स्टैक से पॉप किया जाता है, फिर भी क्योंकि लौटा हुआ लैम्ब्डा सीधे भाग नहीं जाता है, क्योंकि यह वापस आ गया है, एक संक्षिप्त अवधि के लिए अस्तित्व में एक और संदर्भ नहीं है जब तक कि एक ही संदर्भ न हो जब लैम्ब्डा वास्तव में चलाया जाता है तब वापस धक्का दिया जाता है, इसलिए साझा_ptr की संदर्भ संख्या शून्य हिट नहीं होनी चाहिए और फिर डेटा को हटा देना चाहिए?

जब तक सी ++ 11 का लैम्ब्डा कैप्चरिंग एक बड़ा सौदा नहीं है, तो मैं इसे क्रेडिट दे रहा हूं, अगर यह है, तो मैं प्रसन्न हूं। यदि यह मामला है, तो क्या मैं मान सकता हूं कि सी ++ 11 का परिवर्तनीय कैप्चरिंग सभी लेक्सिकल स्कोपिंग/क्लोजर ट्रिकरी को ला लिस्प को तब तक अनुमति देगी जब तक कि कुछ/गतिशील रूप से आवंटित स्मृति की देखभाल कर रहा हो? क्या मैं मान सकता हूं कि सभी कब्जे वाले संदर्भ तब तक जीवित रहेगा जब तक कि लैम्ब्डा स्वयं हटा नहीं जाता है, जिससे मुझे उपरोक्त फैशन में smart_ptrs का उपयोग करने की इजाजत मिलती है?

यदि ऐसा लगता है कि यह है, तो इसका मतलब यह नहीं है कि सी ++ 11 अभिव्यक्तिपूर्ण उच्च-आदेश प्रोग्रामिंग की अनुमति देता है? यदि ऐसा है, तो मुझे लगता है कि सी ++ 11 समिति ने एक उत्कृष्ट नौकरी की है =)

उत्तर

7

लैम्ब्डा from_ref मूल्य से कैप्चर करता है, इसलिए यह एक प्रतिलिपि बनाता है। इस प्रतिलिपि के कारण, from_ref नष्ट हो जाने पर रेफ गिनती 0 नहीं है, यह प्रतिलिपि के कारण 1 है जो अभी भी लैम्ब्डा में मौजूद है।

+1

तो मैं सही समझ में हूँ कि std :: समारोह वस्तु ही उदाहरण की अवधि के दौरान कैप्चर मान संग्रहीत कर लेता साथ स्थानापन्न

return [from_ref]() { return *from_ref += 2; }; 

? और यह इस संदर्भ को संग्रहीत करके, shared_ptr संदर्भ गणना 0 हिट नहीं करती है? ओह समझा। कितना सुरुचिपूर्ण – Louis

+3

@ लुइस नो, 'फ़ंक्शन' ऑब्जेक्ट नहीं, लेकिन लैम्ब्डा। 'std :: function' lambdas के कैप्चर के बारे में नहीं जानता है। –

+0

तो संदर्भ के कैप्चरिंग और स्टोरेज को std :: function instance में किसी सदस्य के रूप में संग्रहीत करने के बजाय एक विशेष मामले के रूप में संभाला जाता है। धन्यवाद। – Louis

4

निम्नलिखित:

std::shared_ptr<int> from_ref(new int(from)); 
return [from_ref]() { return *from_ref += 2; }; 

इस ज्यादातर बराबर है:

std::shared_ptr<int> from_ref(new int(from)); 
class __uniqueLambdaType1432 { 
    std::shared_ptr<int> capture1; 
public:   
    __uniqueLambdaType1432(std::shared_ptr<int> capture1) : 
    capture1(capture1) { 
    } 
    decltype(*capture1 += 2) operator()() const { 
    return *capture1 += 2; 
    } 
}; 
return __uniqueLambdaType1432(from_ref); 

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

1

से_ref मूल्य से कब्जा कर लिया गया है।

आपका तर्क काम करता है अगर आप

return [&from_ref]() { return *from_ref += 2; }; 
संबंधित मुद्दे