फ़ंक्शन पॉइंटर और एसटीडी पर अस्पष्ट ओवरलोड को हल करना :: + का उपयोग करके एक लंबो के लिए फ़ंक्शन


93

निम्नलिखित कोड में, पहला कॉल fooअस्पष्ट है, और इसलिए संकलन करने में विफल रहता है।

दूसरा, +लंबोदर से पहले जोड़ा गया , फ़ंक्शन पॉइंटर ओवरलोड का समाधान करता है।

#include <functional>

void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }

int main ()
{
    foo(  [](){} ); // ambiguous
    foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}

क्या है +अंकन यहां क्या कर?

जवाबों:


98

+अभिव्यक्ति में +[](){}एकल है +ऑपरेटर। इसे इस प्रकार परिभाषित किया गया है: [expr.unary.op] / 7:

यूनिरी +ऑपरेटर के संचालकों में अंकगणित, असंगत संलयन या सूचक प्रकार होंगे और परिणाम तर्क का मूल्य होगा।

मेमना अंकगणित प्रकार आदि का नहीं है, लेकिन इसे परिवर्तित किया जा सकता है:

[Expr.prim.lambda] / 3

लंबोदर-अभिव्यक्ति का प्रकार [...] एक अद्वितीय, अनाम गैर-संघी वर्ग प्रकार है - जिसे क्लोजर प्रकार कहा जाता है - जिसके गुणों का वर्णन नीचे किया गया है।

[Expr.prim.lambda] / 6

लैम्ब्डा-एक्सप्रेशन के लिए लैम्बडा-कैप्चर के साथ क्लोजर टाइप में पॉइंटर के फंक्शन कॉल ऑपरेटर के रूप में एक ही पैरामीटर और रिटर्न टाइप करने के लिए पॉइंटर के लिए एक publicनॉन virtual- नॉन - explicit constकन्वर्सेशन फंक्शन है । इस रूपांतरण फ़ंक्शन द्वारा लौटाया गया मान किसी फ़ंक्शन का पता होगा, जिसे लागू करते समय, क्लोजर प्रकार के फ़ंक्शन कॉल ऑपरेटर को लागू करने के समान प्रभाव पड़ता है।

इसलिए, unary +फ़ंक्शन पॉइंटर प्रकार में रूपांतरण को बाध्य करता है, जो इस लंबोदर के लिए है void (*)()। इसलिए, अभिव्यक्ति +[](){}का प्रकार यह फ़ंक्शन पॉइंटर प्रकार है void (*)()

void foo(void (*f)())ओवरलोड रिज़ॉल्यूशन के लिए रैंकिंग में दूसरा अधिभार एक सटीक मैच बन जाता है और इसलिए इसे असंदिग्ध रूप से चुना जाता है (क्योंकि पहला अधिभार एक सटीक मिलान नहीं है)।


लैम्ब्डा [](){}में बदला जा सकता std::function<void()>की गैर स्पष्ट टेम्पलेट ctor के माध्यम से std::functionहै, जो किसी भी प्रकार कि पूरा करता है लेता है Callableऔर CopyConstructibleआवश्यकताओं।

लैम्ब्डा void (*)()को क्लोजर प्रकार (ऊपर देखें) के रूपांतरण फ़ंक्शन के माध्यम से भी परिवर्तित किया जा सकता है ।

दोनों उपयोगकर्ता-परिभाषित रूपांतरण अनुक्रम और एक ही रैंक के हैं। इसीलिए अस्पष्टता के कारण पहले उदाहरण में ओवरलोड रिज़ॉल्यूशन विफल हो जाता है।


डैनियल क्रुगलर के एक तर्क द्वारा समर्थित कैसियो नेरी के अनुसार, इस एकरी +चाल को निर्दिष्ट व्यवहार होना चाहिए, अर्थात आप इस पर भरोसा कर सकते हैं (टिप्पणियों में चर्चा देखें)।

फिर भी, यदि आप अस्पष्टता से बचना चाहते हैं, तो मैं फ़ंक्शन पॉइंटर प्रकार के लिए एक स्पष्ट कलाकारों का उपयोग करने की सलाह दूंगा: आपको SO पर यह पूछने की आवश्यकता नहीं है कि क्या होता है और यह क्यों काम करता है;)


3
@Fred AFAIK सदस्य फ़ंक्शन पॉइंटर्स को गैर-सदस्य फ़ंक्शन पॉइंटर्स में परिवर्तित नहीं किया जा सकता है, अकेले फ़ंक्शन लैवल्यूज़ दें। आप किसी सदस्य फ़ंक्शन को ऑब्जेक्ट के माध्यम std::bindसे बाँध std::functionसकते हैं जिसे फ़ंक्शन लैवल्यू के समान कहा जा सकता है।
dyp

2
@ डीपीपी: मेरा मानना ​​है कि हम मुश्किल पर भरोसा कर सकते हैं। दरअसल, मान लीजिए कि एक कार्यान्वयन operator +()एक स्टेटलेस क्लोजर प्रकार में जोड़ता है। मान लें कि यह ऑपरेटर पॉइंटर के अलावा कुछ और काम करता है ताकि क्लोजर टाइप को परिवर्तित किया जा सके। फिर, यह एक प्रोग्राम के अवलोकन योग्य व्यवहार को बदल देगा जो 5.1.2 / 3 का उल्लंघन करता है। कृपया, मुझे बताएं कि क्या आप इस तर्क से सहमत हैं।
कैसियो नेरी

2
@ कैसियोनेरी हां, यह वह बिंदु है जहां मुझे यकीन नहीं है। मैं मानता हूं कि अवलोकन योग्य व्यवहार एक को जोड़ने पर बदल सकता है operator +, लेकिन यह उस स्थिति की तुलना कर रहा है जिसके operator +साथ शुरू करने के लिए नहीं है। लेकिन यह निर्दिष्ट नहीं है कि क्लोजर प्रकार का operator +अधिभार नहीं होगा । "एक कार्यान्वयन क्लोजर प्रकार को अलग-अलग परिभाषित कर सकता है जो कि नीचे वर्णित है, बशर्ते कि यह कार्यक्रम के अवलोकन योग्य व्यवहार को बदल नहीं सकता है [...]" के अलावा, लेकिन एक ऑपरेटर को जोड़ने वाला IMO क्लोजर प्रकार को कुछ से अलग नहीं बदलता है "नीचे वर्णित है"।
dyp

3
@ डीवाईपी: वह स्थिति जहां कोई नहीं operator +()है, मानक द्वारा वर्णित एक बिल्कुल है। मानक एक कार्यान्वयन को निर्दिष्ट निर्दिष्ट से कुछ अलग करने की अनुमति देता है। उदाहरण के लिए, जोड़ना operator +()। हालाँकि, यदि यह अंतर किसी प्रोग्राम द्वारा अवलोकन योग्य है, तो यह अवैध है। एक बार जब मैंने comp.lang.c ++ में पूछा। यदि बंद करने वाला टाइप टाइप कर सकता है result_typeऔर दूसरे typedefsको उनके अनुकूल (उदाहरण के लिए std::not1) अनुकूल बनाने के लिए । मुझे बताया गया कि ऐसा नहीं हो सकता क्योंकि यह देखने योग्य था। मैं लिंक खोजने की कोशिश करूँगा।
कैसियो नेरी

6
VS15 आपको यह मजेदार त्रुटि देता है: test.cpp (543): त्रुटि C2593: 'ऑपरेटर +' अस्पष्ट t \ test.cpp (543) है: ध्यान दें: 'अंतर्निहित C ++ ऑपरेटर + (शून्य (__cdecl *) हो सकता है (शून्य) )) 't \ test.cpp (543): नोट: या' अंतर्निहित C ++ ऑपरेटर + (शून्य (__stdcall *)) (शून्य) 't \ test.cpp (543): ध्यान दें: या अंतर्निहित C ++ ऑपरेटर + (void (__fastcall *) (शून्य)) 't \ test.cpp (543): नोट: या' बिल्ट-इन C ++ ऑपरेटर + (void (__vectorcall *)) (void)) t \ test.cpp (543): नोट : तर्क सूची '(wmain :: <lambda_d983319760d11be517b3d48b95b3fe58>) test.cpp (543): त्रुटि C2088: + +: कक्षा के लिए
एड लाम्बर्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.