एक ही नाम के साथ लैम्ब्डा कैप्चर और पैरामीटर - कौन दूसरे को छाया देता है? (clang बनाम gcc)


125
auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • क्लैंग ++ 3.6.0 और नए प्रिंट आउट "आप क्लैंग ++ का उपयोग कर रहे हैं!" और कब्जा foo अप्रयुक्त होने के बारे में चेतावनी दी ।

  • g ++ 4.9.0 और नया प्रिंट आउट "आप g ++ का उपयोग कर रहे हैं!" और पैरामीटर foo अप्रयुक्त होने के बारे में चेतावनी दी ।

यहाँ C ++ स्टैंडर्ड के बाद कौन सा कंपाइलर अधिक सटीक है?

वैंडबॉक्स उदाहरण


1
वैंडबॉक्स से कोड को यहाँ तक पेस्ट करना (उन्हें लगता है कि शेयर बटन भूल गए हैं) यह VS2015 (?) की तरह प्रतीत होता है, जो C4458 की चेतावनी देते हुए इस बात से सहमत है : 'फू' छुपाता है क्लास सदस्य
nwp

12
महान उदाहरण ..
deviantfan

4
लैम्बडा के पास एक प्रकार है जिसमें टेम्पलेट फ़ंक्शन कॉल ऑपरेटर होता है, इस प्रकार तर्क मुझे कहता है कि पैरामीटर को कैप्चर किए गए चर को छाया देना चाहिए जैसे कि struct Lambda { template<typename T> void operator()(T foo) const { /* ... */ } private: decltype(outer_foo) foo{outer_foo}; }
स्केजजैक

2
@nwp VS गलत है, लैम्ब्डा के डेटा सदस्य अनाम हैं और इस प्रकार इसे छायांकित नहीं किया जा सकता है। मानक कहते हैं, "एक कैप्चर की गई इकाई तक पहुंच संबंधित डेटा सदस्य तक पहुंचने के लिए बदल जाती है", जो हमें वर्ग एक पर छोड़ देती है।
एन। 'सर्वनाम' मी।

10
मुझे उम्मीद है कि क्लैंग संस्करण सही है - अगर किसी फ़ंक्शन के बाहर फ़ंक्शन पैरामीटर के चारों ओर कुछ अन्य तरीके के बजाय, यह नया आधार तोड़ रहा होगा!
MM

जवाबों:


65

अद्यतन करें: जैसा कि नीचे बोली में कोर कुर्सी द्वारा वादा किया गया है, अब कोड बीमार है :

यदि एक साधारण-कैप्चर में एक पहचानकर्ता लैम्बडा-घोषणाकर्ता के पैरामीटर-डिक्लेरेशन-क्लॉज के पैरामीटर के डिक्लेरेटर-आईडी के रूप में प्रकट होता है , तो प्रोग्राम बीमार है।


कुछ समय पहले लंबोदर में नाम देखने से संबंधित कुछ समस्याएं थीं। वे N2927 द्वारा हल किए गए थे :

नया शब्द अब कैप्चरिंग संस्थाओं के उपयोगों को हटाने के लिए लुकअप पर निर्भर नहीं करता है। यह अधिक स्पष्ट रूप से व्याख्याओं से इनकार करता है कि एक लैम्ब्डा के कंपाउंड-स्टेटमेंट को दो पास में संसाधित किया जाता है या उस कंपाउंड-स्टेटमेंट में कोई भी नाम क्लोजर प्रकार के सदस्य को हल कर सकता है।

लुकअप हमेशा लैंबडा-एक्सप्रेशन के संदर्भ में किया जाता है , कभी नहीं "के बाद" एक क्लोजर प्रकार के सदस्य फ़ंक्शन बॉडी में परिवर्तन के बाद। देखें [expr.prim.lambda] / 8 :

लैम्ब्डा अभिव्यक्ति के यौगिक बयान पैदावार समारोह शरीर समारोह कॉल ऑपरेटर की ([dcl.fct.def]), लेकिन नाम देखने के प्रयोजनों के लिए, [...], यौगिक बयान के संदर्भ में माना जाता है लैम्ब्डा अभिव्यक्ति । [ उदाहरण :

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};

- अंतिम उदाहरण ]

(उदाहरण यह भी स्पष्ट करता है कि लुकअप किसी तरह से क्लोजर प्रकार के उत्पन्न कैप्चर सदस्य पर विचार नहीं करता है।)

नाम fooकैप्चर में घोषित (पुनः) नहीं है; यह लंबोदर अभिव्यक्ति को घेरने वाले ब्लॉक में घोषित किया गया है। पैरामीटर fooएक ब्लॉक में घोषित किया जाता है जो उस बाहरी ब्लॉक में निहित है (देखें [basic.scope.block] / 2 , जिसमें स्पष्ट रूप से लंबो मापदंडों का भी उल्लेख है)। देखने का क्रम आंतरिक से बाहरी ब्लॉकों तक स्पष्ट रूप से है । इसलिए पैरामीटर का चयन किया जाना चाहिए, अर्थात्, क्लैंग सही है।

यदि आप कैप्चर को इन-कैप्चर बनाना चाहते हैं, तो foo = ""इसके बजाय foo, उत्तर स्पष्ट नहीं होगा। ऐसा इसलिए है क्योंकि कब्जा अब वास्तव में एक घोषणा को प्रेरित करता है जिसका "ब्लॉक" नहीं दिया गया है। मैंने इस पर कोर चेयर को मैसेज किया, जिसने जवाब दिया

यह समस्या 2211 है (शीघ्र ही एक नई समस्या सूची खुले-std.org साइट पर दिखाई देगी, दुर्भाग्यवश कई मुद्दों के लिए सिर्फ प्लेसहोल्डर्स के साथ, जिनमें से यह एक है; मैं उन अंतरालों को भरने के लिए कड़ी मेहनत कर रहा हूं पहले Kona महीने के अंत में बैठक)। सीडब्ल्यूजी ने हमारे जनवरी के टेलीकॉन्फ्रेंस के दौरान इस पर चर्चा की, और यह निर्देश प्रोग्राम को बीमार बनाने के लिए है यदि कैप्चर नाम भी एक पैरामीटर नाम है।


मेरे लिए यहां कुछ भी नहीं चीरना :) एक साधारण-कब्जा कुछ भी घोषित नहीं करता है, इसलिए नाम देखने का सही परिणाम काफी स्पष्ट है (यदि आप स्पष्ट कैप्चर के बजाय कैप्चर-डिफॉल्ट का उपयोग करते हैं तो BTW, GCC सही हो जाता है )। init-Capture s कुछ पेचीदा हैं।
टीसी

1
@TC मैं सहमत हूं। मैंने एक मुख्य मुद्दा दायर किया, लेकिन जाहिर तौर पर इस पर पहले ही चर्चा हो चुकी है, संपादित जवाब देखें।
कोलंबो

6

मैं आपको सार्थक उत्तर देने के लिए प्रश्न के साथ कुछ टिप्पणियों को पैक करने की कोशिश कर रहा हूं।
सबसे पहले, ध्यान दें कि:

  • गैर-स्थैतिक डेटा सदस्यों को प्रत्येक प्रति-कैप्चर किए गए चर के लिए लैम्बडा के लिए घोषित किया जाता है
  • विशिष्ट मामले में, लैम्ब्डा में एक क्लोजर प्रकार होता है, जिसमें एक सार्वजनिक इनलाइन टेम्पलेट फ़ंक्शन कॉल ऑपरेटर होता है, जिसका नाम एक पैरामीटर होता है foo

इसलिए तर्क मुझे पहली नज़र में कहेंगे कि पैरामीटर को कैप्चर किए गए चर को छाया देना चाहिए जैसे कि:

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};

वैसे भी, @nm ने सही रूप से नोट किया कि कॉपी-कैप्चर किए गए चर के लिए घोषित गैर-स्थैतिक डेटा सदस्य वास्तव में अनाम हैं। कहा जा रहा है कि, अनाम डेटा सदस्य को अभी भी एक पहचानकर्ता (यानी foo) द्वारा एक्सेस किया जाता है । इसलिए, फ़ंक्शन कॉल ऑपरेटर का पैरामीटर नाम अभी भी होना चाहिए (मुझे कहना चाहिए) छाया कि पहचानकर्ता
जैसा कि सही टिप्पणी में @nm द्वारा सही ढंग से बताया गया है:

मूल कैप्चर की गई इकाई [...] को सामान्य रूप से स्कोप नियमों के अनुसार छायांकित किया जाना चाहिए

उसके कारण, मैं कहूंगा कि क्लैंग सही है।


जैसा कि ऊपर बताया गया है, इस संदर्भ में लुकअप कभी नहीं किया जाता है जैसे कि हम परिवर्तित क्लोजर प्रकार में थे।
कोलंबो

@ कोलुम्बो मैं एक ऐसी लाइन जोड़ रहा हूं जिसे मैं याद कर रहा था भले ही वह तर्क से स्पष्ट हो, लेकिन यह है कि क्लैंग सही है। मजेदार बात यह है कि मैंने उत्तर देने की कोशिश करते समय [expr.prim.lambda] / 8 पाया, लेकिन मैं इसे ठीक से उपयोग नहीं कर पाया जैसा आपने किया। इसलिए हर बार आपके उत्तरों को पढ़ने में खुशी मिलती है। ;-)
स्केपजैक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.