2012-07-24 8 views
17

मैं सी ++ 11 कार्यात्मक सुविधाओं के साथ खेल रहा हूं। एक चीज जो मुझे अजीब लगता है वह यह है कि लैम्ब्डा फ़ंक्शन का प्रकार वास्तव में <> प्रकार नहीं है। और क्या है, लैम्ब्डा टाइप-इनफ्रेंसिंग तंत्र के साथ वास्तव में अच्छी तरह से खेलना प्रतीत नहीं होता है।C++ 11 में लैम्ब्डा फ़ंक्शन क्यों कार्य नहीं करते <> प्रकार?

संलग्न एक छोटा सा उदाहरण है, जिसमें मैं दो पूर्णांकों जोड़ने के लिए एक समारोह के दो तर्क flipping का परीक्षण किया है। (संकलक मैं इस्तेमाल किया MinGW के तहत जीसीसी 4.6.2 था।) उदाहरण में, addInt_f के लिए प्रकार स्पष्ट रूप से समारोह <> का उपयोग करते समय addInt_l एक लैम्ब्डा जिसका प्रकार auto साथ टाइप-inferenced है परिभाषित किया गया है।

जब मैं कोड संकलित, flip समारोह addInt की स्पष्ट रूप से परिभाषित टाइप संस्करण नहीं बल्कि लैम्ब्डा संस्करण स्वीकार कर सकते हैं, कह रही है कि, testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'

अगले कुछ लाइनों

कि लैम्ब्डा संस्करण को दिखाने में कोई त्रुटि दे रही है (साथ ही 'कच्चा' संस्करण) स्वीकार किया जा सकता है अगर यह स्पष्ट रूप से उपयुक्त फ़ंक्शन <> प्रकार पर डाला गया है।

तो मेरी प्रश्न हैं:

  1. क्यों यह एक लैम्ब्डा समारोह पहली जगह में एक function<> प्रकार नहीं है कि है? छोटे उदाहरण में, addInt_l में function<int (int,int)> क्यों भिन्न, lambda प्रकार के बजाय प्रकार के रूप में नहीं है? कार्यात्मक प्रोग्रामिंग के परिप्रेक्ष्य से, फ़ंक्शन/कार्यात्मक वस्तु और लैम्ब्डा के बीच क्या अंतर है?

  2. अगर वहाँ एक मौलिक कारण इन दो अलग अलग होना जरूरी है। मैंने सुना है कि लैम्ब्डा को function<> में परिवर्तित किया जा सकता है लेकिन वे अलग हैं। क्या यह सी ++ 11 का एक डिज़ाइन मुद्दा/दोष है, एक कार्यान्वयन मुद्दा है या क्या दोनों को इस तरह से अलग करने में कोई लाभ है? ऐसा लगता है कि addInt_l के प्रकार-हस्ताक्षर ने पैरामीटर के पैरामीटर और रिटर्न प्रकारों के बारे में पर्याप्त जानकारी प्रदान की है।

  3. वहाँ एक रास्ता तो यह है कि ऊपर उल्लेख किया है स्पष्ट प्रकार कास्टिंग बचा जा सकता है लैम्ब्डा लिखने के लिए है?

अग्रिम धन्यवाद।

//-- testCppBind.cpp -- 
    #include <functional> 
    using namespace std; 
    using namespace std::placeholders; 

    template <typename T1,typename T2, typename T3> 
    function<T3 (T2, T1)> flip(function<T3 (T1, T2)> f) { return bind(f,_2,_1);} 

    function<int (int,int)> addInt_f = [](int a,int b) -> int { return a + b;}; 
    auto addInt_l = [](int a,int b) -> int { return a + b;}; 

    int addInt0(int a, int b) { return a+b;} 

    int main() { 
     auto ff = flip(addInt_f); //ok 
     auto ff1 = flip(addInt_l); //not ok 
     auto ff2 = flip((function<int (int,int)>)addInt_l); //ok 
     auto ff3 = flip((function<int (int,int)>)addInt0); //ok 

     return 0; 
    } 

+4

आपको 'std :: function' तर्क नहीं लेना चाहिए, मुख्य रूप से क्योंकि यह प्रकार की कटौती को रोकता है (जो आपकी समस्या है)। –

+1

संबंधित: [सी ++ 11 टाइप नहीं करता है जब std :: function या lambda फ़ंक्शन शामिल होते हैं] (http://stackoverflow.com/q/9998402/487781) – hardmath

+1

लैम्बडास अज्ञात फ़ंक्शंस में परिवर्तित हो जाते हैं (या फ़ंक्शन यदि वे पर्यावरण पर कब्जा न करें)। उन्हें std :: फ़ंक्शन में बदलने से भाषा और पुस्तकालय के बीच एक मजबूत युग्मन शुरू हो जाएगा और इसलिए यह एक बहुत बुरा विचार होगा। – MFH

उत्तर

39

std::function एक उपकरण दुकान प्रतिदेय वस्तु के किसी भी प्रकार अपने प्रकार की परवाह किए बिना के लिए उपयोगी है। ऐसा करने के लिए इसे कुछ प्रकार की मिरर तकनीक को नियोजित करने की आवश्यकता है, और इसमें कुछ ओवरहेड शामिल है।

किसी भी कॉल करने योग्य को std::function में स्पष्ट रूप से परिवर्तित किया जा सकता है, और यही कारण है कि यह आमतौर पर निर्बाध रूप से काम करता है।

मुझे यकीन है कि यह स्पष्ट हो जाता बनाने के लिए दोहराने की आवश्यकता होगी: std::function सिर्फ lambdas या समारोह संकेत के लिए कुछ नहीं है: यह प्रतिदेय के किसी भी प्रकार के लिए है। इसमें उदाहरण के लिए struct some_callable { void operator()() {} }; जैसी चीजें शामिल हैं। यही कारण है कि एक साधारण से एक है, लेकिन यह इस बजाय की तरह कुछ हो सकता है:

struct some_polymorphic_callable { 
    template <typename T> 
    void operator()(T); 
}; 

एक लैम्ब्डा है बस अभी तक एक और प्रतिदेय वस्तु, ऊपर some_callable वस्तु के उदाहरण के समान है। इसे std::function में संग्रहीत किया जा सकता है क्योंकि यह कॉल करने योग्य है, लेकिन इसमें std::function के प्रकार का ओवरहेड नहीं है।

और समिति भविष्य में लैम्बडास पॉलिमॉर्फिक बनाने की योजना बना रही है, यानी, लैम्बडा जो some_polymorphic_callable जैसा दिखता है। कौन सा std::function प्रकार ऐसा लैम्ब्डा होगा?


अब ... टेम्पलेट पैरामीटर कटौती, या निहित रूपांतरण। एक चुनें। यह सी ++ टेम्पलेट का नियम है।

std::function तर्क के रूप में लैम्ब्डा को पास करने के लिए, इसे पूरी तरह से परिवर्तित करने की आवश्यकता है। std::function तर्क लेना मतलब है कि आप कटौती के प्रकार पर निहित रूपांतरण चुन रहे हैं। लेकिन आपके फ़ंक्शन टेम्पलेट को हस्ताक्षर करने या स्पष्ट रूप से प्रदान किए जाने वाले हस्ताक्षर की आवश्यकता होती है।

समाधान? अपने कॉलर्स को std::function पर सीमित न करें। किसी भी प्रकार के कॉल करने योग्य स्वीकार करें।

template <typename Fun> 
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1)) 
{ return std::bind(std::forward<Fun>(f),_2,_1); } 

अब आप सोच सकते क्यों हम std::function तो जरूरत नहीं है। std::function एक ज्ञात हस्ताक्षर के साथ कॉलबेल के लिए प्रकार मिटा देता है। यह अनिवार्य रूप से टाइप-मिस्ड कॉलबेल स्टोर करने और virtual इंटरफेस लिखने के लिए उपयोगी बनाता है।

+0

: आपके स्पष्ट स्पष्टीकरण के लिए धन्यवाद। आईएमएचओ, तथ्य यह है कि std :: फ़ंक्शन केवल भंडारण के लिए उपयोग किया जा सकता है बल्कि निराशाजनक है। अगर मैं आपका जवाब सही ढंग से समझता हूं, तो यह ओवरहेड से बचने के लिए है? फिर, क्या स्मार्ट पॉइंटर्स के लिए भी यही है? साथ ही, आपके 'some_polymorphic_callable' उदाहरण में, मुझे नहीं लगता कि फ़ंक्शन <> प्रकार टेम्पलेट लैम्बडा के फ़ंक्शन के बाद से प्रकार को व्यक्त नहीं कर सकता है <> पहले से ही टेम्पलेट पैरा हो सकते हैं। यदि ओवरहेड एकमात्र मुद्दा है, तो क्या किसी ने हल्के वजन वाले <> पैरामीटर प्रकारों में उपयोग किया जा सकता है? यह एसटीएल को और अधिक उपयोग करने योग्य बना देगा। – tinlyx

+0

इसके अलावा, 'decltype' उदाहरण में, यदि फ़ंक्शन बॉडी एक लाइनर के बजाय कोड की 25 लाइनें है, तो क्या हमें decltype में 25 लाइनों को शामिल करने की आवश्यकता है? इसके अलावा, '' का उपयोग लगभग शून्य * या मेरे लिए एक मैक्रो जैसा दिखता है। 'फन' का गलत उपयोग केवल तभी किया जा सकता है जब संकलन त्रुटि होती है। तुलनात्मक रूप से, एक फ़ंक्शन <> पैरामीटर प्रकार (यदि उपयोग योग्य) स्पष्ट रूप से उस व्यक्ति और कंप्यूटर को प्रकार हस्ताक्षर बताएगा। माना जाता है कि, मेरे पास "टाइप एरर" और इसी तरह के बारे में बहुत सीमित ज्ञान है। नियमों को बताने के लिए धन्यवाद। मैं बस सोच रहा हूं कि ऐसा क्यों होना चाहिए। – tinlyx

+0

@TingL पॉलिमॉर्फिक लैम्ब्डा के लिए प्रकार व्यक्त नहीं किया जा सकता है क्योंकि 'फ़ंक्शन <>' * एक * हस्ताक्षर तर्क लेता है, जबकि पॉलिमॉर्फिक लैम्ब्डा * अलग-अलग हस्ताक्षरों की एक असंबद्ध संख्या होगी * (यही कारण है कि इसे पॉलिमॉर्फिक कहा जाता है: इसमें कई रूप हैं)। ओवरहेड मुद्दा स्मार्ट पॉइंटर्स के लिए बिल्कुल लागू नहीं होता है। 'unique_ptr' वास्तव में * शून्य * ओवरहेड है (यह डिज़ाइन लक्ष्यों में से एक था)। यदि आप एक बहुप्रचारित प्रोग्राम नहीं चला रहे हैं, तो 'shared_ptr' कुछ ओवरहेड हो सकता है, लेकिन बहुप्रचारित प्रोग्राम ऐसे मामले हैं जहां इसका उपयोग होने की अधिक संभावना है। –

5
  1. क्योंकि function<>type erasure कार्यरत हैं। यह function<> में कई अलग-अलग फ़ंक्शन-जैसे प्रकारों को संग्रहीत करने की अनुमति देता है, लेकिन एक छोटे से रनटाइम जुर्माना लगाता है। टाइप एरर वर्चुअल फ़ंक्शन इंटरफ़ेस के पीछे वास्तविक प्रकार (आपका विशिष्ट लैम्ब्डा) छुपाता है।
  2. इस के लिए एक लाभ है: सी ++ डिजाइन "सूक्तियों" में से एक भूमि के ऊपर जोड़ने कभी नहीं जब तक कि यह वास्तव में जरूरत है। इस सेटअप का उपयोग करते समय, आपके पास टाइप अनुमान का उपयोग करते समय कोई ओवरहेड नहीं है (auto का उपयोग करें या टेम्पलेट पैरामीटर के रूप में पास करें), लेकिन आपके पास अभी भी function<> के माध्यम से गैर-टेम्पलेट कोड के साथ इंटरफ़ेस करने के लिए सभी लचीलापन है। यह भी ध्यान रखें कि function<> एक भाषा निर्माण नहीं है, लेकिन मानक पुस्तकालय का एक घटक जिसे सरल भाषा सुविधाओं का उपयोग करके कार्यान्वित किया जा सकता है।
  3. नहीं है, लेकिन आप समारोह में लिख सकते हैं बस function<> (पुस्तकालय निर्माण) की बारीकियों के बजाय समारोह (भाषा निर्माण) के प्रकार के लेने के लिए।बेशक, यह वास्तव में रिटर्न प्रकार को लिखना बहुत मुश्किल बनाता है, क्योंकि यह आपको सीधे पैरामीटर प्रकार नहीं देता है। हालांकि, कुछ मेटा-प्रोग्रामिंग का उपयोग करके ला Boost.FunctionTypes आप इन फ़ंक्शन से प्राप्त कर सकते हैं। ऐसे कुछ मामले हैं जहां यह संभव नहीं है, उदाहरण के लिए ऐसे फ़ैक्टर वाले जिनके पास operator() है।
संबंधित मुद्दे