2017-02-21 8 views
8

मैंने एक टेम्पलेट कोड लिखा है जो एक मज़ेदार को तर्क के रूप में लेता है और कुछ प्रसंस्करण के बाद इसे निष्पादित करता है। यद्यपि कोई और उस कार्य को एक लैम्ब्डा, एक फ़ंक्शन पॉइंटर या यहां तक ​​कि std::function पास कर सकता है लेकिन यह मुख्य रूप से लैम्ब्डा के लिए है (नहीं कि मैं अन्य प्रारूपों पर प्रतिबंध लगाता हूं)। मैं पूछना चाहता हूं कि मुझे उस लैम्ब्डा को कैसे लेना चाहिए - मूल्य से? संदर्भ से? या कुछ और।तर्क के रूप में lambda गुजर रहा है - संदर्भ या मूल्य से?

उदाहरण कोड -

template<typename Functor> 
    void f(Functor functor) 

template<typename Functor> 
    void f(Functor &functor) 

template<typename Functor> 
    void f(Functor &&functor) 

बेहतर तरीका और क्या और क्यों हो सकता है -

#include <iostream> 
#include <functional> 
using namespace std; 

template<typename Functor> 
void f(Functor functor) 
{ 
    functor(); 
} 

void g() 
{ 
    cout << "Calling from Function\n"; 
} 

int main() 
{ 
    int n = 5; 

    f([](){cout << "Calling from Temp Lambda\n";}); 

    f([&](){cout << "Calling from Capturing Temp Lambda\n"; ++n;}); 

    auto l = [](){cout << "Calling from stored Lambda\n";}; 
    f(l); 

    std::function<void()> funcSTD = []() { cout << "Calling from std::Function\n"; }; 
    f(funcSTD); 

    f(g); 
} 

उपरोक्त कोड में, मैं यह इनमें से किसी बनाने का एक विकल्प है? क्या इनमें से किसी के लिए कोई सीमाएं हैं?

+0

आईएमएचओ, मूल्य से गुजरता है। – NathanOliver

+1

मैंने हमेशा मूल्य से गुजरने वाले फंक्चर टेम्पलेटेड ऑब्जेक्ट्स को देखा है, लेकिन मुझे नहीं पता क्यों; अच्छा प्रश्न। – YSC

+2

संबंधित/डुप्ले: http://stackoverflow.com/questions/8196345/passing-functor-object-by-value-vs-by-reference-c – NathanOliver

उत्तर

4

संभावित दोष के रूप में, ध्यान दें कि प्रतिलिपि से गुजरना अगर काम नहीं करता है तो लैम्ब्डा कॉपी करने योग्य नहीं है। यदि आप इससे दूर हो सकते हैं, प्रतिलिपि से गुजरना ठीक है।
एक उदाहरण के रूप:

#include<memory> 
#include<utility> 

template<typename F> 
void g(F &&f) { 
    std::forward<F>(f)(); 
} 

template<typename F> 
void h(F f) { 
    f(); 
} 

int main() { 
    auto lambda = [foo=std::make_unique<int>()](){}; 

    g(lambda); 
    //h(lambda); 
} 

ऊपर स्निपेट में, lambdafoo की वजह से copyable नहीं है। इसकी कॉपी कन्स्ट्रक्टर इस तथ्य के परिणामस्वरूप हटा दिया गया है कि std::unique_ptr की कॉपी कन्स्ट्रक्टर हटा दी गई है।
दूसरी तरफ, F &&f दोनों लालच और रावल संदर्भों को एक अग्रेषण संदर्भ के साथ-साथ कॉन्स्ट संदर्भों को स्वीकार करता है।
अन्य शर्तों में, यदि आप को एक ही लैम्ब्डा को एक से अधिक तर्क के रूप में पुन: उपयोग करना चाहते हैं, तो आप नहीं कर सकते हैं कि आपके कार्यों को कॉपी करके आपकी वस्तु मिलती है और आपको इसे कॉपी करने योग्य नहीं है (ठीक है, वास्तव में आप कर सकते हैं, यह है इसे एक लैम्ब्डा में लपेटने का मामला जो बाहरी संदर्भ को संदर्भित करता है)।

+0

लेकिन 'एच (std :: move (lambda));' होगा काम। – NathanOliver

+2

@NathanOliver बेशक, लेकिन आप अब lambda का पुन: उपयोग नहीं कर सकते हैं। यदि आप इसे उदाहरण के रूप में दो कार्यों में पास करना चाहते हैं, तो यह संभव नहीं है। – skypjack

+0

@skypjack फॉलोअप ques - क्या दो समान अधिभारों को परिभाषित करने के अलावा इस तरह के परिदृश्य में चलने योग्य मज़ेदार के लिए फ़ॉलबैक करना संभव है .. –

3

चूंकि लैम्ब्डा अभिव्यक्तियों के अपने क्षेत्र (कक्षाओं की तरह) हो सकते हैं, प्रतिलिपि/संदर्भ का उपयोग अलग-अलग परिणाम हो सकता है। यहाँ सरल उदाहरण है: अगर आप प्रदर्शन से वहाँ वास्तव में कोई अंतर नहीं है यह न्याय करना चाहते

template<class func_t> 
size_t call_copy(func_t func) { 
    return func(1); 
} 

template<class func_t> 
size_t call_ref(func_t& func) { 
    return func(1); 
} 

int main() { 
    auto lambda = [a = size_t{0u}] (int val) mutable { 
     return (a += val); 
    }; 
    lambda(5); 

    // call_ref(lambda); – uncomment to change result from 5 to 6 
    call_copy(lambda); 

    std::cout << lambda(0) << std::endl; 

    return 0; 
} 

Btw, lambdas बहुत छोटे और कॉपी करने के लिए आसान कर रहे हैं।

इसके अलावा - यदि आप लैम्ब्डा को पैरामीटर के रूप में पास करना चाहते हैं (इसमें कोई चर नहीं है) तो आपको फ़ॉरवर्डिंग संदर्भ का उपयोग करना चाहिए ताकि यह काम कर सके।

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