इस सवाल का पूरी तरह से कोड में जवाब नहीं दिया जा सकता है। आप कुछ हद तक "समतुल्य" कोड लिखने में सक्षम हो सकते हैं, लेकिन मानक उस तरह से निर्दिष्ट नहीं है।
उस रास्ते से बाहर, चलो में गोता [expr.prim.lambda]
। ध्यान देने वाली पहली बात यह है कि कंस्ट्रक्टर केवल में उल्लिखित हैं [expr.prim.lambda.closure]/13
:
लैम्ब्डा-एक्सप्रेशन से जुड़े क्लोजर टाइप का कोई डिफॉल्ट कंस्ट्रक्टर नहीं होता है अगर लैम्ब्डा-एक्सप्रेशन में लैम्बडा-कैप्चर होता है और डिफॉल्ट डिफॉल्ट कंस्ट्रक्टर होता है। इसमें एक डिफॉल्ट कॉपी कंस्ट्रक्टर और एक डिफॉल्ट मूव कंस्ट्रक्टर ([class.copy.ctor]) है। यदि यह एक नष्ट कर दिया प्रतिलिपि असाइनमेंट ऑपरेटर है लैम्ब्डा अभिव्यक्ति एक है लैम्ब्डा पर कब्जा और डिफॉल्ट की कॉपी और चाल काम ऑपरेटरों अन्यथा ([class.copy.assign])। [ नोट: इन विशेष सदस्य कार्यों को सामान्य रूप से परिभाषित किया गया है, और इसलिए इन्हें हटाए जाने के रूप में परिभाषित किया जा सकता है। - अंतिम नोट ]
तो बल्ले से सही, यह स्पष्ट होना चाहिए कि निर्माणकर्ता औपचारिक रूप से नहीं हैं कि वस्तुओं को कैसे परिभाषित किया जाए। आप बहुत करीब हो सकते हैं (cppinsights.io उत्तर देखें), लेकिन विवरण अलग-अलग हैं (ध्यान दें कि केस 4 के लिए उस उत्तर में कोड कैसे संकलित नहीं करता है)।
केस 1 पर चर्चा करने के लिए ये मुख्य मानक खंड हैं:
[expr.prim.lambda.capture]/10
[...]
प्रतिलिपि द्वारा कब्जा की गई प्रत्येक इकाई के लिए, एक अनाम गैर-स्थैतिक डेटा सदस्य को क्लोजर प्रकार में घोषित किया जाता है। इन सदस्यों का घोषणा आदेश अनिर्दिष्ट है। इस तरह के डेटा सदस्य का प्रकार संदर्भित प्रकार है यदि इकाई किसी वस्तु का संदर्भ है, तो संदर्भित फ़ंक्शन प्रकार का एक अंतराल संदर्भ यदि इकाई किसी फ़ंक्शन का संदर्भ है, या संबंधित कैप्चर की गई इकाई का प्रकार अन्यथा। किसी अनाम यूनियन के सदस्य को कॉपी द्वारा कैप्चर नहीं किया जाएगा।
[expr.prim.lambda.capture]/11
लैम्ब्डा-एक्सप्रेशन के कंपाउंड-स्टेटमेंट के भीतर प्रत्येक आईडी-एक्सप्रेशन , जो कॉपी द्वारा कैप्चर की गई इकाई का ओड-यूज़ है, क्लोजर टाइप के संबंधित अनाम डेटा सदस्य तक पहुंच में तब्दील हो जाता है। [...]
[expr.prim.lambda.capture]/15
जब लंबोदर-अभिव्यक्ति का मूल्यांकन किया जाता है, तो प्रतिलिपि द्वारा कैप्चर की गई संस्थाओं का उपयोग परिणामस्वरूप क्लोजर ऑब्जेक्ट के प्रत्येक संबंधित गैर-स्थिर डेटा सदस्य को प्रत्यक्ष-प्रारंभ करने के लिए किया जाता है, और गैर-स्थैतिक डेटा सदस्य जो कि init-captures के अनुरूप होते हैं, प्रारंभिक रूप से होते हैं। संबंधित इनिशियलाइज़र (जो कॉपी-या प्रत्यक्ष-इनिशियलाइज़ेशन हो सकता है) द्वारा इंगित किया गया है। [...]
आइए इसे अपने मामले में लागू करें 1:
केस 1: मूल्य द्वारा कब्जा / मूल्य द्वारा डिफ़ॉल्ट कब्जा
int x = 6;
auto lambda = [x]() { std::cout << x << std::endl; };
इस लैम्ब्डा के बंद प्रकार में एक गैर-स्थैतिक डेटा सदस्य (चलो इसे कॉल करें __x
) प्रकार का होगा int
(क्योंकि x
न तो एक संदर्भ है और न ही कोई फ़ंक्शन है), और x
लैम्ब्डा बॉडी के भीतर एक्सेस करने के लिए इसे एक्सेस किया जाता है __x
। जब हम लैम्ब्डा एक्सप्रेशन का मूल्यांकन करते हैं (यानी जब असाइन करते हैं lambda
), तो हम डायरेक्ट-इनिशियलाइज़ __x
करते हैं x
।
संक्षेप में, केवल एक प्रति लगती है । क्लोजर प्रकार का निर्माता शामिल नहीं है, और इसे "सामान्य" सी ++ में नोट करना संभव नहीं है (ध्यान दें कि क्लोजर प्रकार एक समग्र प्रकार भी नहीं है)।
संदर्भ पर कब्जा शामिल [expr.prim.lambda.capture]/12
:
एक इकाई को संदर्भ द्वारा कैप्चर किया जाता है यदि यह अंतर्निहित या स्पष्ट रूप से कैप्चर किया गया हो लेकिन प्रतिलिपि द्वारा कैप्चर नहीं किया गया हो। यह अनिर्दिष्ट है कि क्या अतिरिक्त अनाम गैर-स्थैतिक डेटा सदस्यों को संदर्भ द्वारा कैप्चर की गई संस्थाओं के लिए क्लोजर प्रकार में घोषित किया गया है। [...]
संदर्भों के संदर्भ पर कब्जा करने के बारे में एक और पैराग्राफ है लेकिन हम कहीं भी ऐसा नहीं कर रहे हैं।
तो, केस 2 के लिए:
केस 2: संदर्भ द्वारा कैप्चर करें / संदर्भ द्वारा डिफ़ॉल्ट कैप्चर करें
int x = 6;
auto lambda = [&x]() { std::cout << x << std::endl; };
हमें पता नहीं है कि कोई सदस्य क्लोजर प्रकार में जोड़ा जाता है या नहीं। x
भेड़ के बच्चे के शरीर में सीधे x
बाहर का उल्लेख हो सकता है । यह संकलक पर निर्भर है, और यह मध्यवर्ती भाषा (जो संकलक से संकलक में भिन्न होती है) के कुछ रूप में करेगी, न कि C ++ कोड का स्रोत परिवर्तन।
Init कैप्चर विस्तृत हैं [expr.prim.lambda.capture]/6
:
इन-कैप्चर व्यवहार करता है जैसे कि यह घोषित करता है और स्पष्ट रूप से उस रूप के एक चर को पकड़ता है auto init-capture ;
जिसका घोषणात्मक क्षेत्र लंबोदर-अभिव्यक्ति का यौगिक-बयान है, सिवाय इसके:
- (६.१) यदि कैप्चर कॉपी (नीचे देखें) से होता है, तो गैर-स्टेटिक डेटा मेंबर को कैप्चर और वैरिएबल के लिए घोषित किया जाता है, जिसे एक ही ऑब्जेक्ट को संदर्भित करने के दो अलग-अलग तरीकों के रूप में माना जाता है, जिसमें गैर-स्टैटिक डेटा का जीवनकाल होता है। सदस्य, और कोई अतिरिक्त प्रतिलिपि और विनाश नहीं किया जाता है, और
- (6.2) यदि कैप्चर संदर्भ से होता है, तो क्लोजर ऑब्जेक्ट का जीवनकाल समाप्त होने पर चर का जीवनकाल समाप्त हो जाता है।
यह देखते हुए, आइए केस 3 देखें:
केस 3: सामान्यीकृत init कैप्चर
auto lambda = [x = 33]() { std::cout << x << std::endl; };
जैसा कि कहा गया है, इसे एक चर के रूप में कल्पना करें auto x = 33;
और प्रतिलिपि द्वारा स्पष्ट रूप से कब्जा कर लिया जाए। यह चर लैम्ब्डा शरीर के भीतर केवल "दृश्यमान" है। जैसा कि [expr.prim.lambda.capture]/15
पहले उल्लेख किया गया है, __x
लैम्बडा अभिव्यक्ति के मूल्यांकन पर दिए गए इनिशलाइज़र द्वारा क्लोजर प्रकार ( पश्चात के लिए) के संबंधित सदस्य का आरंभीकरण है।
संदेह से बचने के लिए: इसका मतलब यह नहीं है कि चीजों को यहां दो बार शुरू किया गया है। auto x = 33;
एक "के रूप में यदि" सरल कैप्चर के शब्दों के वारिस है, और वर्णित आरंभीकरण उन शब्दों के एक संशोधन है। केवल एक इनिशियलाइज़ेशन होता है।
यह मामला 4 को भी कवर करता है:
auto l = [p = std::move(unique_ptr_var)]() {
// do something with unique_ptr_var
};
क्लोजर टाइप मेंबर को इनिशियलाइज़ किया जाता है __p = std::move(unique_ptr_var)
जब लैम्ब्डा एक्सप्रेशन का मूल्यांकन किया जाता है (यानी जब l
असाइन किया गया हो)। करने के लिए पहुंच p
लैम्ब्डा शरीर में करने के लिए पहुंच के रूप में तब्दील कर रहे हैं __p
।
टीएल; डीआर: केवल प्रतियां / इनिशियलाइज़ / मूव्स की न्यूनतम संख्या का प्रदर्शन किया जाता है (जैसा कि कोई उम्मीद / उम्मीद करेगा)। मैं मानूंगा कि लैम्बदास एक स्रोत परिवर्तन (अन्य संश्लिष्ट शर्करा के विपरीत) के संदर्भ में निर्दिष्ट नहीं हैं, क्योंकि निर्माणकर्ताओं के संदर्भ में चीजों को व्यक्त करने के लिए अत्यधिक संचालन की आवश्यकता होगी।
मुझे आशा है कि यह प्रश्न में व्यक्त आशंकाओं को सुलझाता है :)