2015-09-09 5 views
15

This answer सी ++ 14 में लैम्ब्डा के भीतर एक चर को कैप्चर करने के तरीके को बताता है।लैम्ब्डा को स्थानांतरित करना: एक बार जब आप एक चाल-प्रकार के प्रकार पर कब्जा कर लेते हैं, तो लैम्ब्डा का उपयोग कैसे किया जा सकता है?

लेकिन एक बार जब आप लैम्ब्डा के भीतर एक गैर-प्रतिलिपि योग्य वस्तु (जैसे std::unique_ptr) को स्थानांतरित कर लेते हैं, तो आप लैम्ब्डा की प्रतिलिपि नहीं बना सकते हैं।

अगर आप चल सकता लैम्ब्डा यह ठीक होगा, लेकिन जब ऐसा करने के लिए कोशिश कर रहा एक संकलन त्रुटि मिलती है:

using namespace std; 

class HasCallback 
{ 
    public: 
    void setCallback(std::function<void(void)>&& f) 
    { 
     callback = move(f); 
    } 

    std::function<void(void)> callback; 
}; 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    HasCallback hc; 
    hc.setCallback(
     [uniq = move(uniq)](void) 
     { 
     std::cout << *uniq << std::endl; 
     }); 

    hc.callback(); 
} 

यह g++ साथ निम्न त्रुटि पैदा करता है (मैं नकल करने का प्रयास किया केवल प्रासंगिक लाइन):

error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’ 

... जिसका अर्थ है, मुझे लगता है, कि मेरे लैम्ब्डा स्थानांतरित करने के लिए प्रयास विफल हो गया।

clang++ एक समान त्रुटि देता है।

मैंने लैम्ब्डा में स्पष्ट रूप से move कोशिश की (हालांकि यह एक अस्थायी मूल्य है), लेकिन इससे मदद नहीं मिली।

संपादित करें: नीचे दिए गए उत्तर उपरोक्त कोड द्वारा उत्पादित संकलन त्रुटियों को पर्याप्त रूप से संबोधित करते हैं। वैकल्पिक दृष्टिकोण के लिए, release अद्वितीय सूचक का लक्ष्य मान std::shared_ptr में, जो कॉपी किया जा सकता है। (मैं इसे एक उत्तर के रूप में नहीं लिख रहा हूं, क्योंकि यह मान लेगा कि यह एक एक्सवाई समस्या है, लेकिन अंतर्निहित कारण unique_ptr का उपयोग लैम्ब्डा में नहीं किया जा सकता है जो std::function में परिवर्तित हो जाता है समझने के लिए महत्वपूर्ण है।)

संपादित 2: हिंसक रूप से पर्याप्त, मुझे अभी एहसास हुआ कि auto_ptr वास्तव में सही चीज़ (!), जहां तक ​​मैं कह सकता हूं, सही काम करता हूं। यह अनिवार्य रूप से unique_ptr की तरह कार्य करता है, लेकिन चाल-निर्माण के स्थान पर प्रति-निर्माण की अनुमति देता है।

+0

मुझे लगता है कि setCallback rvalue संदर्भ के बजाय मूल्य द्वारा पैरामीटर मिलना चाहिए, मैं गलत कर रहा हूँ? – Slava

+0

@ स्लावा जो मैंने मूल रूप से किया था, लेकिन यह वही त्रुटि दे दी। मैंने सोचा था कि रावल्यू संदर्भ लेने से लैम्ब्डा को स्थानांतरित करने की अनुमति मिल जाएगी (लेकिन बल), लेकिन ऐसा नहीं लगता है। –

उत्तर

14

आप लैम्ब्डा स्थानांतरित कर सकते हैं, यह ठीक है। यद्यपि आपकी समस्या यह नहीं है कि आप एक गैर-लोकप्रिय लैम्ब्डा के साथ std::function को तुरंत चालू करने का प्रयास कर रहे हैं। और: function की

template< class F > 
function(F f); 

निर्माता करता है:

5) एक प्रतिलिपि f की के साथ लक्ष्य आरंभीकृत।

इसका कारण यह है std::function:

संतुष्ट CopyConstructible और CopyAssignable की आवश्यकताओं।

चूंकि function को प्रतिलिपि बनाना है, जो भी आप इसमें डालते हैं उसे कॉपी करने योग्य भी होना चाहिए। और एक चाल-केवल लैम्ब्डा उस आवश्यकता को पूरा नहीं करता है।

+0

.... हुह। धन्यवाद - मुझे इस साधारण मामले के लिए भी उन त्रुटि संदेशों को पार्स करने में बहुत मुश्किल समय था। क्या हस्ताक्षर को काम में बदलने का कोई तरीका है, 'std :: function' के बजाय सार्वभौमिक-पुनर्प्राप्त-योग्य टेम्पलेट पैरामीटर का उपयोग करने से कम? –

+0

@KyleStrand इससे कोई फर्क नहीं पड़ता कि पैरामीटर क्या है, आप बस 'फ़ंक्शन' नहीं बना सकते हैं। अगर आपको कुछ प्रकार की मिटाने की ज़रूरत है, तो आपको एक चलने योग्य 'फंक्शन' समकक्ष लिखना होगा। – Barry

+0

@ बैरी वहां केवल std :: फ़ंक्शंस विकल्प हैं जो पहले से ही दो का उल्लेख करने के लिए हैं: https://github.com/Naios/Function2 और https://github.com/potswa/cxx_function –

10

std::function लैम्ब्डा नहीं है!यह एक रैपर है जिसे किसी भी प्रकार के कॉलबल से बनाया जा सकता है, जिसमें लैम्ब्डा भी शामिल है। std::function की आवश्यकता है कि callable be copy-constructible, यही कारण है कि आपका उदाहरण विफल हो जाता है।

एक चाल-केवल लैम्ब्डा को नीचे दिखाए गए अनुसार फिर से स्थानांतरित किया जा सकता है।

template<typename F> 
void call(F&& f) 
{ 
    auto f1 = std::forward<F>(f); // construct a local copy 
    f1(); 
} 

int main() 
{ 
    auto uniq = make_unique<std::string>("Blah blah blah"); 
    auto lambda = [uniq = move(uniq)]() { 
     std::cout << *uniq << std::endl; 
     }; 
// call(lambda); // doesn't compile because the lambda cannot be copied 
    call(std::move(lambda)); 
} 

Live demo

+0

एएके। मुझे पता था कि 'std :: function' lambda नहीं है, लेकिन चूंकि लैम्ब्डा परिवर्तनीय है' std :: function' में मैं अपने दिमाग में भेद स्पष्ट नहीं रख रहा था। –

+0

तो मूल कोड में हैसकॉलबैक को कैसे कार्यान्वित करें? मेरा मतलब यह है कि, चाल-केवल लैम्ब्डा को केवल एक कंटेनर में स्थानांतरित करने या केवल डेटा संरचना को स्थानांतरित करने के लिए कैसे सहेजना है? – alpha

+0

@alpha आपको या तो इसे किसी अन्य प्रकार में परिवर्तित किए बिना सीधे लैम्ब्डा का उपयोग करने की आवश्यकता होगी (यानी वह कार्य जो लैम्ब्डा को कॉल करता है उसे लैम्ब्डा को टेम्पलेट-प्रकार तर्क के रूप में लेने की आवश्यकता होगी), या एक अलग फ़ंक्शन-क्लास का उपयोग करें (वहां हैं जंगली में 'std :: function' के विभिन्न विकल्प, या आप अपना खुद का डिज़ाइन कर सकते हैं)। –

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