अस्वीकरण: मेरा उत्तर वास्तविकता की तुलना में कुछ हद तक सरलीकृत है (मैंने कुछ विवरण अलग रखा है) लेकिन बड़ी तस्वीर यहां है। साथ ही, मानक पूरी तरह से निर्दिष्ट नहीं करता है कि कैसे lambdas या std::function
आंतरिक रूप से कार्यान्वित किया जाना चाहिए (कार्यान्वयन में कुछ स्वतंत्रता है) ताकि कार्यान्वयन के विवरण पर किसी भी चर्चा की तरह, आपका कंपाइलर इस तरह से ऐसा कर सकता है या नहीं।
लेकिन फिर, यह एक विषय VTables के समान ही है: मानक को अधिक जरूरी नहीं है लेकिन किसी भी समझदार कंपाइलर को अभी भी ऐसा करने की संभावना है, इसलिए मेरा मानना है कि इसमें थोड़ा खोदना उचित है।
auto lambda = [](Args...) -> Return { /*...*/ };
// roughly equivalent to:
struct {
Return operator()(Args...) { /*...*/ }
}
lambda; // instance of the anonymous struct
बस किसी भी अन्य वर्ग की तरह
, जब आप अपने चारों ओर अपने उदाहरणों पारित कभी नहीं: :)
lambdas
एक लैम्ब्डा लागू करने के लिए सबसे सरल तरीका एक गुमनाम struct
की तरह है कोड को प्रतिलिपि बनाना है, केवल वास्तविक डेटा (यहां, बिल्कुल भी नहीं)।
वस्तुओं मूल्य द्वारा कब्जा कर लिया struct
में नकल कर रहे हैं:
Value v;
auto lambda = [=](Args...) -> Return { /*... use v, captured by value...*/ };
// roughly equivalent to:
struct Temporary { // note: we can't make it an anonymous struct any more since we need
// a constructor, but that's just a syntax quirk
const Value v; // note: capture by value is const by default unless the lambda is mutable
Temporary(Value v_) : v(v_) {}
Return operator()(Args...) { /*... use v, captured by value...*/ }
}
lambda(v); // instance of the struct
फिर, उसके चारों ओर गुजर केवल मतलब है कि आप डेटा (v
) नहीं कोड में ही गुजरती हैं।
इसी तरह, वस्तुओं संदर्भ द्वारा कब्जा कर लिया struct
में संदर्भित हैं:
Value v;
auto lambda = [&](Args...) -> Return { /*... use v, captured by reference...*/ };
// roughly equivalent to:
struct Temporary {
Value& v; // note: capture by reference is non-const
Temporary(Value& v_) : v(v_) {}
Return operator()(Args...) { /*... use v, captured by reference...*/ }
}
lambda(v); // instance of the struct
कि काफी सब है जब यह खुद को lambdas के लिए (कुछ कार्यान्वयन विवरण मैं लोप को छोड़कर आता है, लेकिन जो प्रासंगिक नहीं हैं यह समझने के लिए कि यह कैसे काम करता है)।
std::function
std::function
functor (lambdas, स्टैंडअलोन/स्थिर/सदस्य काम करता है, लोगों की तरह functor वर्गों मैं से पता चला है, ...) के किसी भी प्रकार के चारों ओर एक सामान्य आवरण है।
std::function
के आंतरिक बहुत जटिल हैं क्योंकि उन्हें उन सभी मामलों का समर्थन करना होगा। functor का सही प्रकार पर निर्भर करता है इस की आवश्यकता है कम से कम निम्नलिखित डेटा (दे या कार्यान्वयन विवरण लेने के लिए):
- एक स्टैंडअलोन/स्थिर कार्य करने के लिए एक सूचक।
या,
- एक प्रति करने के लिए एक सूचक functor की (गतिशील functor किसी भी प्रकार की अनुमति देने के लिए आवंटित, जैसा कि आप ठीक ही यह ध्यान दिया) [नीचे नोट देखें]।
- सदस्य समारोह के लिए एक सूचक कहा जाता है।
- एक आवंटक के लिए एक सूचक जो दोनों को मज़ेदार और खुद की प्रतिलिपि बनाने में सक्षम है (चूंकि किसी भी प्रकार के मज़ेदार का उपयोग किया जा सकता है, इसलिए पॉइंटर-टू-फ़ैक्टर
void*
होना चाहिए और इस प्रकार ऐसा तंत्र होना चाहिए - शायद polymorphism उर्फ। बेस क्लास + आभासी तरीकों, व्युत्पन्न वर्ग template<class Functor> function(Functor)
रचनाकारों में स्थानीय रूप से उत्पन्न किया जा रहा है)।
चूंकि यह पहले से पता है नहीं functor किस तरह यह स्टोर करने के लिए होगा (और इस तथ्य यह है कि std::function
पुन: असाइन किया जा सकता द्वारा स्पष्ट किया जाता है) तो यह सब संभव मामलों से निपटने के लिए है और यह फैसला लेने के लिए चलने के समय पर।
नोट: मैं जहां स्टैंडर्ड जनादेश यह पता नहीं है, लेकिन यह निश्चित रूप से एक नई प्रतिलिपि है, अंतर्निहित functor साझा नहीं है:
int v = 0;
std::function<void()> f = [=]() mutable { std::cout << v++ << std::endl; };
std::function<void()> g = f;
f(); // 0
f(); // 1
g(); // 0
g(); // 1
इसलिए, जब आप एक std::function
आसपास पारित इसमें कम से कम उन चार पॉइंटर्स शामिल हैं (और वास्तव में जीसीसी 4.7 64 बिट्स sizeof(std::function<void()>
32 है जो चार 64 बिट पॉइंटर्स हैं) और वैकल्पिक रूप से मज़ेदार की गतिशील रूप से आवंटित प्रति (जिसे मैंने पहले ही कहा है, में केवल कब्जे वाले ऑब्जेक्ट्स हैं, आप कोड कॉपी न करें)।
सवाल
इस तरह एक समारोह के लिए एक लैम्ब्डा गुजर की लागत क्या है करने के लिए उत्तर?[प्रश्न के संदर्भ: मूल्य द्वारा]
ठीक है, आप इसे अपने functor पर मुख्य रूप से निर्भर करता है (या तो एक हाथ से बनाई गई struct
functor या एक लैम्ब्डा) और चर इसमें देख सकते हैं। ओवरहेड सीधे struct
फ़ैक्टर द्वारा मूल्य से गुजरने की तुलना में काफी नगण्य है, लेकिन यह संदर्भ के अनुसार struct
फ़ैक्टर को पार करने से काफी अधिक है।
मैं प्रत्येक कार्य वस्तु const&
के साथ पारित चिह्नित करने के लिए इतना है कि एक प्रति नहीं किया जाता है है चाहिए?
मुझे डर है कि एक सामान्य तरीके से जवाब देना बहुत मुश्किल है। कभी-कभी आप const
संदर्भ से गुजरना चाहते हैं, कभी-कभी मूल्य से, कभी-कभी रैवल्यू संदर्भ से ताकि आप इसे स्थानांतरित कर सकें। यह वास्तव में आपके कोड के अर्थशास्त्र पर निर्भर करता है।
नियम जो आपको चुनना चाहिए, वह एक बिल्कुल अलग विषय आईएमओ है, बस याद रखें कि वे किसी अन्य वस्तु के समान हैं।
वैसे भी, अब आपके पास एक सूचित निर्णय लेने के लिए सभी कुंजी हैं (फिर, आपके कोड और इसके अर्थशास्त्र के आधार पर)।
अच्छा सवाल है, यह भी जानना चाहते :) – Xaqq