2012-04-14 7 views
5

वहाँ लैम्ब्डा समारोह चर का उपयोग करने के दो तरीके हैं: क्योंकि मैं करते हैं, पता करने के लिए मतभेद हैं क्यालैम्ब्डा समारोह चर सी ++ 11

std::function<int(int, int)>* x2 = new std::function<int(int, int)>([=](int a, int b) -> int{return a + b;}); 

//usage 
void set(std::function<int(int, int)>* x); 
std::function<int(int, int)>* get(); 

मैं चाहूँगा:

std::function<int(int, int)> x1 = [=](int a, int b) -> int{return a + b;}; 

//usage 
void set(std::function<int(int, int)> x); 
std::function<int(int, int)> get(); 

और यह नहीं पता कि लैम्ब्डा फ़ंक्शन डेटा कैसे संग्रहीत किया जाता है।

मैं छेड़छाड़, स्मृति उपयोग और लैम्बडा फ़ंक्शन को तर्क या वापसी लैम्बडा फ़ंक्शन के रूप में पास करने का सबसे अच्छा तरीका जानने के लिए सबसे अच्छा तरीका जानना चाहता हूं।
यदि मैं लैम्ब्डा फ़ंक्शन ऑब्जेक्ट का आकार 4 से बड़ा है या त्रुटियों से बचने के लिए पॉइंटर्स का उपयोग करना पसंद करूंगा (अगर किसी प्रकार की कॉपी कन्स्ट्रक्टर निष्पादित की जाती है जब मैं एट्रिब्यूशन करता हूं या किसी प्रकार का विनाशक निष्पादित होता है जब मैं नहीं करता चाहते हैं)।

मुझे लैम्ब्डा फ़ंक्शन वेरिएबल्स कैसे घोषित करना चाहिए?

संपादित

मैं प्रतियां और चालें से बचना चाहते हैं, मैं एक ही समारोह फिर से उपयोग करने के लिए जारी रखना चाहते हैं।

मुझे इस उदाहरण को कैसे बदलना चाहिए?

int call1(std::function<int(int, int)> f){ 
    return f(1, 2); 
} 
int call2(std::function<int(int, int)> f){ 
    return f(4, 3); 
} 
std::function<int(int, int)>& recv(int s){ 
    return [=](int a, int b) -> int{return a*b + s;}; 
} 

int main(){ 
    std::function<int(int, int)> f1, f2; 

    f1 = [=](int a, int b) -> int{return a + b;}; 
    f2 = recv(10); 

    call1(f1); 
    call2(f1); 
    call1(f2); 
    call2(f2); 
} 

मैं वापस नहीं लौट सकते समारोह recv में संदर्भ:

warning: returning reference to temporary 

यह एक अच्छा समाधान है?

int call1(std::function<int(int, int)>* f){ 
    return (*f)(1, 2); 
} 
int call2(std::function<int(int, int)>* f){ 
    return (*f)(4, 3); 
} 

std::function<int(int, int)>* recv(int s){ 
    return new std::function<int(int, int)>([=](int a, int b) -> int{return a*b + s;}); 
} 

int main(){ 
    std::function<int(int, int)> f1 = [=](int a, int b) -> int{return a + b;}; 
    std::function<int(int, int)> *f2 = recv(10); 

    call1(&f1); 
    call2(&f1); 
    call1(f2); 
    call2(f2); 

    delete f2; 
} 

संपादित करें (निष्कर्ष)

एक लैम्ब्डा समारोह वस्तु किसी भी वस्तु एक वर्ग के उदाहरण है कि तरह है। आवंटन, तर्क और विशेषता के नियम समान हैं।

+9

"अगर मैं लैम्ब्डा फ़ंक्शन ऑब्जेक्ट का आकार 4 से बड़ा है तो मैं पॉइंटर्स का उपयोग करना पसंद करूंगा" क्यों? क्या आपको लगता है कि अगर 4 बाइट्स से बड़ा ऑब्जेक्ट उस पर है तो स्टैक फुल जाएगा? मानक सी ++ अभ्यास ऑब्जेक्ट को * डिफ़ॉल्ट * द्वारा स्टैक पर रखना है, जब तक कि आपके पास कोई अच्छा कारण न हो। और 4 बाइट से बड़ा होने वाला एक नहीं है। –

उत्तर

5

आप यह स्पष्ट है कि आप संकेत का उपयोग करना चाहते कर दिया है। तो ... ऐसा करो।

लेकिन कम से कम स्मार्ट सूचक का उपयोग करें। std::shared_ptr का उचित उपयोग कई समस्याओं को रोक देगा। बेशक, आपको परिपत्र संदर्भों से बचने के लिए सुनिश्चित करना होगा, लेकिन लैम्बडास के लिए, यह असंभव प्रतीत होता है।

माना जाता है कि यह अभी भी एक भयानक विचार है।लेकिन यह आपके कोड के वास्तविक चलने वाले समय में कोई वास्तविक मापनीय लाभ होने के बजाय, चीजों को पार करने के बारे में बेहतर महसूस करने के लिए पूरी तरह से व्यर्थ समयपूर्व अनुकूलन होने के अर्थ में केवल भयानक है। कम से कम shared_ptr के साथ, आप स्मृति को गलती से हटाने या अपवाद सुरक्षा समस्याओं में चलाने की संभावना कम हैं।


मैं ढेर: बँटवारा कुछ बुरा सी ++ अभ्यास आप बस के रूप में आसानी से आवंटित ढेर कर सकता है, साथ ही एक स्मार्ट सूचक के उपयोग के बिना इस स्मृति पर नज़र रखने के दर्द को अनदेखा करने के लिए जा रहा हूँ। यह मान लेगा कि आप जानते हैं कि आप क्या कर रहे हैं और इसके बजाय आपने "प्रदर्शन" और "स्मृति उपयोग" मुद्दों पर ध्यान केंद्रित किया होगा।

इसे आप जो करना चाहते हैं उसके समर्थन के रूप में न लें।

std::function आमतौर पर कुछ वस्तु के आंतरिक ढेर आवंटन द्वारा कार्यान्वित किया जाएगा; टाइप-एरर के कारण यह जरूरी है। तो यह आकार में कम से कम एक सूचक होगा; संभवतः अधिक ढेर आवंटन का आकार एक vtable सूचक + आपके लैम्ब्डा वर्ग का आकार होगा। और इसका आकार इस बात से शासित है कि आप कितनी चीजें कैप्चर करते हैं और क्या यह मूल्य या संदर्भ से है।

अधिकांश सी ++ मानक पुस्तकालय वस्तुओं की तरह, std::function कॉपी करने योग्य है। एक प्रतिलिपि बनाना वास्तव में प्रति कॉपी करेगा, एक नाटक प्रतिलिपि न करें जहां अब आपके पास एक ही सूचक के साथ दो ऑब्जेक्ट्स हों। इसलिए प्रत्येक के पास आंतरिक ढेर ऑब्जेक्ट की एक स्वतंत्र प्रति होगी। यही है, इसे कॉपी करने का मतलब एक और ढेर आवंटन करना होगा। यह लैम्ब्डा की प्रतिलिपि भी प्रतिलिपि करेगा, जिसका अर्थ यह है कि इसमें सब कुछ कॉपी किया गया है।

हालांकि, अधिकांश सी ++ मानक पुस्तकालय वस्तुओं की तरह, std::functionचलने योग्य है। यह कोई मेमोरी आवंटन करता है।

इसलिए, यदि आप ऐसा करना चाहते हैं, यह बहुत सस्ता है: के रूप में की जरूरत

std::function<int(int, int)> x1 = [=](int a, int b) -> int{return a + b;}; 

//usage 
void set(std::function<int(int, int)> x); 
const std::function<int(int, int)> &get(); 

set(std::move(x1)); //x1 is now *empty*; you can't use it anymore. 

आपका set समारोह अपनी ही आंतरिक संग्रहण में स्थानांतरित कर सकते हैं। ध्यान दें कि get अब const& लौटाता है; यह उन कार्यों के लिए भंडारण पर आकस्मिक है जो कहीं भी नहीं जा रहे हैं।

move कितना सस्ता होगा? यह शायद std::function के क्षेत्रों की प्रतिलिपि बनाने के साथ ही मूल को खाली या अन्यथा बेअसर करने के बराबर होगा। जब तक आप प्रदर्शन-महत्वपूर्ण कोड में न हों, तब तक ऐसा कुछ भी नहीं होगा जिसके बारे में आपको कभी चिंतित होना चाहिए।

+0

मैं ढेर पर आवंटित संदर्भ वापस नहीं कर सकता। मैं चाल का उपयोग नहीं कर सकता, क्योंकि मुझे फिर से फ़ंक्शन का उपयोग करने की आवश्यकता होगी, मुझे लगता है कि पारंपरिक सूचक मदद करेगा। – Squall

+0

@ स्क्वाल: आपको 'मिल' फ़ंक्शन शायद किसी ऑब्जेक्ट या किसी चीज़ के सदस्य जैसे कुछ स्टोरेज से पुनर्प्राप्त कर रहा है। और यदि नहीं, तो बस एक मूल्य वापस करें और एनवीआरओ या चाल-निर्माण को इसकी देखभाल करें, क्योंकि आप एक स्टैक पैरामीटर के बारे में बात कर रहे हैं। और नहीं, "पारंपरिक सूचक" सहायक नहीं है (और अब सी ++ में "पारंपरिक" नहीं है)। यह सब कुछ और बग बना देगा। यदि आपको अभी भी ऑब्जेक्ट का उपयोग करने की आवश्यकता है, तो इसका उपयोग * पहले से * या बस * कॉपी * करें। अन्यथा, आपको यह पता लगाना होगा कि इसका मालिक कौन है, जो स्मार्ट पॉइंटर्स के बिना बहुत त्रुटि प्रवण है। अपवाद सुरक्षा का उल्लेख नहीं है। –

+0

@ स्क्वाल: ऐसा लगता है कि आप पहले से ही तय कर चुके हैं कि आप क्या करना चाहते हैं, लेकिन आप जानते हैं कि यह कोशेर नहीं है, और आपकी योजना के लिए सत्यापन ढूंढने की कोशिश कर रहा है। यदि आप सभी की सलाह को अनदेखा करने के लिए तैयार हैं और केवल ढेर-उन्हें नग्न पॉइंटर्स में आवंटित करते हैं और "पारंपरिक" चीज (पारंपरिक मेमोरी लीक, डबल-डिलीशन और अपवाद सुरक्षा की कमी सहित) करते हैं, तो * इसे * करें। यदि आप वास्तविक सलाह चाहते हैं, तो ऑब्जेक्ट की प्रतिलिपि बनाएँ और/या इसे स्थानांतरित करें, जैसा आपको चाहिए। या * बहुत * कम से कम, एक स्मार्ट सूचक का उपयोग करें। –

15

std::function के सूचक बनाने के लिए यह समझ में नहीं आता है। यह किसी भी तरह से आपको लाभ नहीं पहुंचाता है। लाभों के बजाय, यह आपको अधिक बोझ डालता है, क्योंकि आपको स्मृति को हटाना होगा।

तो पहला संस्करण (यानी गैर-सूचक संस्करण) वह सब कुछ है जिसके लिए आपको जाना चाहिए।

सामान्य रूप से, अंगूठे के नियम की तरह, new से जितना संभव हो सके से बचें।

इसके अलावा, जब भी आप लैम्ब्डा का उपयोग करते हैं तो आपको std::function की आवश्यकता नहीं है। कई बार, तुम सिर्फ इस्तेमाल कर सकते हैं auto के रूप में:

auto add = [](int a, int b) { return a + b;}; 

std::cout << add(100,100) << std::endl; 
std::cout << add(120,140) << std::endl; 
+3

इसके अलावा, स्टैक पर चीजें गुजरना आम तौर पर मुफ्त स्टोर पर चीजों को आवंटित करने और उनके लिए पॉइंटर्स पास करने और संकेत लगाने के लिए बहुत तेज है। –

+2

@ सेठ कार्नेगी और मैन्युअल मेमोरी प्रबंधन और अपवाद-असुरक्षा के कारण आपको कोई सिरदर्द नहीं होगा। –

+0

"कई बार" एक खिंचाव का एक सा है। वह स्पष्ट रूप से इसे कहीं और स्टोर करना चाहता है, इसलिए एक्सेसर फ़ंक्शन है। –

0

यहां दिए गए दिशानिर्देश हैं जो मैं इन कार्यात्मक सी ++ "इन नए लैम्ब्डा अभिव्यक्तियों (प्लस std :: function & std :: bind) के साथ पालन करने के लिए करते हैं। उन्हें अनदेखा करने के लिए स्वतंत्र महसूस करें या फिट होने पर उन्हें अपनाने के लिए:

1) किसी भी lambda असाइन करें जिसे आप std :: function के रूप में नाम रखना चाहते हैं। यदि आप इसे कुछ फ़ंक्शन "एक्स (..)" में पास कर रहे हैं और इसे फिर से नहीं देख रहे हैं, तो इसे फ़ंक्शन कॉल में घोषित करें ताकि "एक्स" इस तथ्य का लाभ उठा सके कि यह एक अस्थायी है।

// I want a real name for this (plan to use multiple times, etc.) 
// Rarely are you going to do this without moving the lambda around, so 
// declaring it as 'auto' is pointless because C++ APIs expect a real type. 

std::function<double(double)> computeAbs = [](double d) -> double { return fabs(d); }; 

// Here, we only ever use the lambda for the call to "X". 
// So, just declare it inline. That will enforce our intended usage and probably 
// produce more efficient code too. 

X([](double r) -> double { return fabs(r); }); 

2) आप, हर जगह काम करता है के लिए कदम std :: समारोह के आसपास संदर्भ बार गणना आवरण किसी प्रकार का उपयोग करना चाहते हैं। विचार वास्तविक std :: फ़ंक्शन पर एक सुरक्षित सूचक को पास करना है, इस प्रकार महंगी प्रतियों से बचने के साथ-साथ नए और हटाने के बारे में चिंता करना।

ऐसा करने के लिए अपनी खुद की कक्षा बनाना एक दर्द हो सकता है, लेकिन यह सुनिश्चित करेगा कि आप वास्तव में एक बार std :: फ़ंक्शन में अपने लैम्ब्डा, फ़ंक्शन पीआरटी इत्यादि को परिवर्तित करने के लिए हिट लें। फिर आप एपीआई के लिए मूल्य द्वारा ले जाने वाले std :: फ़ंक्शन को वापस करने के लिए "GetFunction" जैसे एपीआई प्रदान कर सकते हैं जो उन्हें उम्मीद करता है।

3) सार्वजनिक एपीआई के लिए std :: function या lambdas लेते हुए, बस मूल्य से गुजरें।

हां, आप संदर्भ या यहां तक ​​कि आर-वैल्यू (उन '& &') से गुज़रने के लिए काम कर सकते हैं और कुछ परिस्थितियों में वे सही समाधान भी हैं। लेकिन भेड़ का बच्चा बुरा है कि उनका वास्तविक प्रकार सचमुच आपके लिए अनजान है। हालांकि, वे सभी std :: फ़ंक्शन में कनवर्टिबल हैं, वैसे ही सभी आदिम प्रकार 'int' में परिवर्तित हो सकते हैं, इसलिए उन्हें फ़ंक्शंस में पास करना एक ही चाल का उपयोग करना शामिल है जो आपको एक समारोह में "च (int ए) "।

अर्थात्, आपके द्वारा बनाए गए कार्यों "f" है कि या तो की तरह functor तर्क के प्रकार पर टेम्प्लेट की गई:

template<typename Functor> 
double f(Functor absFunc); 

या, आप सब लोग एसटीडी को सामान्य बनाने :: समारोह:

double f(std::function<double(double)> absFunc); 

अब, जब आप "एफ ([] (डबल आर) -> डबल {रिटर्न फैब्स (आर);}) कहते हैं," कंपाइलर जानता है कि इसे कैसे संभालना है। उपयोगकर्ताओं को कुछ ऐसा मिलता है जो आपके एपीआई के आसपास कोड के बिना 'बस काम करता है', जो आप चाहते हैं।