2011-10-22 35 views
18

संभव डुप्लिकेट:
Recursive lambda functions in c++0xरिकर्सिव कॉल (सी ++ 11)

मैं एक लैम्ब्डा क्यों फोन नहीं कर सकते हैं रिकर्सिवली अगर मैं के रूप में यह लिखें:

auto a = [&] 
{ 
    static int i = 0; i++; 
    std::cout << i << std::endl; 
    if (i<10) 
    a(); //recursive call 
}; 

यह संकलन त्रुटि देता है (ideone):

prog.cpp:8:18: error: '((const main()::<lambda()>*)this)->main()::<lambda()>::a' cannot be used as a function 

prog.cpp: In function 'int main()': 
prog.cpp:9:9: error: variable 'auto a' with 'auto' type used in its own initializer 

त्रुटि का क्या अर्थ है?

मैं कारण क्यों मैं यह नहीं लिख सकते हैं:

auto i=i+1; //error: unable to deduce 'auto' from '<expression error>' 

हम इस नहीं लिख सकते हैं क्योंकि i के प्रकार यह आरंभीकरण, किस प्रकार करता है, तो निष्कर्ष निकाला नहीं जा सकता है इसका मतलब है से निष्कर्ष निकाला जाना है i स्वयं प्रारंभ में दिखाई देता है (ideone)। लेकिन लैम्ब्डा के मामले में यह कैसे मायने रखता है? यदि मैं गलत नहीं हूं, तो लैम्ब्डा का प्रकार इसके पैरामीटर (रिटर्न) और रिटर्न प्रकार द्वारा निर्धारित होता है; यह शरीर पर निर्भर नहीं करता है अगर यह कुछ भी नहीं लौटाता है (इस मामले में, वापसी प्रकार को void के रूप में घटाया जाता है, चाहे लैम्ब्डा-बॉडी में अन्य बयानों के बावजूद)।

वैसे भी, मैं एक वैकल्पिक हल मिल गया है, और मैं के रूप में के बजाय std::function उपयोग कर सकते हैं:

std::function<void()> a = [&] 
{ 
    static int i = 0; i++; 
    std::cout << i << std::endl; 
    if (i<10) 
     a(); 
}; 

जो जुर्माना संकलन (ideone)। लेकिन मुझे अभी भी यह जानने में दिलचस्पी है कि auto संस्करण संकलित क्यों नहीं करता है।

+0

मुझे लगता है कि समस्या आपको 'ए' के ​​निहित कैप्चर के साथ समाप्त होने से संबंधित है, एक संदर्भ के रूप में जो असीमित रूप से पुन: कार्य करेगा क्योंकि 'ए' स्वयं 'ए' के ​​सदस्य के रूप में समाप्त होता है। – Flexo

+0

@awoodland: इसका क्या अर्थ है? कृपया उस पर विस्तृत करें। – Nawaz

+3

लैम्ब्डा का प्रकार भी कैप्चर पर निर्भर करता है, जो मुझे संदेह है कि यह मुद्दा यहां है। यदि आप 'ए' का उपयोग नहीं करते हैं तो कंपाइलर परवाह नहीं है, लेकिन जब आप ऐसा करते हैं तो यह नहीं पता कि किस प्रकार की ऑब्जेक्ट का उपयोग करना है। –

उत्तर

15

कारण यह है कि auto चर के लैम्ब्डा-अभिव्यक्ति प्रारंभकर्ताओं के लिए कोई विशेष मामला नहीं है।

ऐसे विशेष मामले त्रुटियों और दुरुपयोगों के लिए प्रवण होंगे। जब आप प्रस्ताव देते हैं कि a() जैसे कुछ काम करना चाहिए तो आपको नियमों को परिभाषित करने की आवश्यकता है। operator() कैसे देखा गया है? a के प्रकार की सटीक स्थिति क्या है? क्या प्रकार पूरा हो जाएगा? (जिसका अर्थ है कि आप पहले से ही लैम्ब्डा की कैप्चर सूची जानते हैं)। एक बार जब आप एक प्रारूप के लिए उचित प्रारूप में तैयार कर लेंगे, तो इस पर बयान देना आसान होगा।

आपके उपयोग के मामले की अनुमति दे अभी तक एक और मामले में जहां आप, क्योंकि a() में a के प्रकार का निर्धारण करने के लिए आप यह सुनिश्चित करें कि प्रारंभकर्ता कुछ भी नहीं के साथ समाप्त होता होना चाहिए कोड में आगे स्कैन करने के लिए की जरूरत का मतलब होगा कि कर सकते थे "unlambda" प्रकार

struct y { void operator()() { } }; 
template<typename T> y operator+(T, y) { return y(); } 
auto x = [] { x(); } + y(); 

इस मामले में, x()y::operator() पर कॉल करेगा, लैम्ब्डा नहीं।

जैसा कि अब है, a को इसके पूरे प्रारंभकर्ता में उल्लिखित करने के लिए मना किया गया है। क्योंकि सी ++ में, auto एक प्रकार नहीं है। यह केवल एक प्रकार निर्दिष्ट एक टू-बी-डिस्प्लेटेड प्रकार के लिए खड़ा है। नतीजतन, एक अभिव्यक्ति में कभी भी ऑटो टाइप नहीं हो सकता है।

+3

" यदि आप संदर्भ द्वारा एक कैप्चर करते हैं, तो आप अपनी लैम्ब्डा की प्रतिलिपि बनाने के बाद एक खतरनाक संदर्भ कॉल करेंगे और मूल दायरे से बाहर हो जाएगा। " - बेशक, सभी कैप्चर-बाय-रेफरेंस इस त्रुटि/दुरुपयोग के लिए प्रवण हैं, इसलिए मुझे संदेह है कि यह संदर्भ में 'ऑटो ए' को कैप्चर करने की अनुमति देने के लिए एक विशेष मामला न प्रदान करने में समिति को प्रेरित करता है। –

+0

'ए' को कैप्चर करने की समस्या के साथ, एक और कारण यह है कि बंद करने के लिए रिकर्सिव कॉल करना संभव नहीं है कि अन्यथा बंद करने वाली वस्तु को संदर्भित करने का कोई तरीका नहीं है: 'यह' संलग्न वर्ग ऑब्जेक्ट को निर्दिष्ट करता है, यदि कोई हो। –

6

मैं इसे देख auto a मामले और std::function<void()> a मामले के बीच महत्वपूर्ण अंतर यह है कि प्रकार std::function<void()> के बारे में/देखभाल क्या असली समारोह के प्रकार यह वास्तव में करने के लिए संदर्भित करता है पता नहीं है। लेखन:

auto a; 

थोड़ा समझ में आता है:

std::function<void()> a; 

बिल्कुल ठीक है, जहां के रूप में है। तो जब आप std::function<void()> का उपयोग करते हैं तो कैप्चर को संश्लेषित करने का समय आता है, जिसे इस प्रकार के बारे में जाना जाना आवश्यक है, जबकि auto के साथ यह अभी तक ज्ञात नहीं है।

+2

मेरा मानना ​​है कि यह सही उत्तर है, और यदि आप कंपाइलर द्वारा उत्पन्न लैम्ब्डा प्रकार का नाम जान सकें (जिसे आप नहीं कर सकते), तो आप उस नाम का उपयोग 'ऑटो' के बजाय घोषित कर सकते हैं। हालांकि, मैं इसे आसानी से "अपरिभाषित व्यवहार" श्रेणी में देख सकता था। –

+0

@ माइकलप्रिस - मैंने इस उत्तर के लिए "आप कभी भी लैम्ब्डा के प्रकार का नाम नहीं जान सकते" पर उद्धरण खोदने का प्रयास किया था, लेकिन मुझे याद नहीं है कि यह कौन सा अनुभाग था। – Flexo

2

एक पुनरावर्ती समारोह f में f द्वारा परिभाषित और वापसी f के प्रकार भी auto के मामले में f से निर्धारित होता है तो यह अनंत प्रत्यावर्तन की ओर जाता है है।

जब auto एक प्रकार प्राप्त करने का प्रयास करता है। decltype (f()) आगे एक अन्य decltype (एफ) 'को कम कर देगा जैसे एफ एफ प्राप्त करने के लिए एफ कुछ भी पुनरावर्ती पर एक कॉल भी रिकर्सिव है। रिकर्सिव फ़ंक्शन पर लागू होने पर वापसी प्रकार निर्धारण पुनरावर्तक हो जाता है। रिकर्सन के रिकर्सिव फ़ंक्शन एंड में रनटाइम पर किया जा सकता है। लेकिन दृढ़ संकल्प स्थिर है केवल

+0

@Neel: यह नहीं है मुझे समझ में आओ जब शरीर में कोई रिटर्न स्टेटमेंट नहीं होता है, तो यह कुछ कैसे वापस कर सकता है? क्या आप यह कहते हैं कि 'गहरे रिकर्सिव लेवल' का अर्थ है कि लैम्ब्डा-बॉडी असीम रूप से बड़ी है कि यह अस्तित्व या वापसी बयान के अस्तित्व को नहीं जान सकता है? – Nawaz

+0

@awoodland: धन्यवाद, मैंने अभी तक टिप्पणियों को देखा है। @ नवाज: प्रत्येक समारोह में केवल विज़ुअलाइज़ेशन के लिए 'रिटर्न' वाक्यविन्यास के लिए कुछ वाक्य रचनात्मक स्कैनिंग वापस करने के लिए गणितीय संभावना मौजूद है।चित्र उचितता के लिए –

+3

+1 –