2015-11-10 12 views
12

मैं एक समारोह के पैरामीटर (foo1/foo2) के संदर्भ में एक ओवरलोड समारोह (bar) के पते को हल करने के साथ प्रयोग कर रहा हूँ का पता।SFINAE और एक ओवरलोड समारोह

struct Baz {}; 

int bar() { return 0; } 
float bar(int) { return 0.0f; } 
void bar(Baz *) {} 

void foo1(void (&)(Baz *)) {} 

template <class T, class D> 
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {} 

int main() { 
    foo1(bar);  // Works 
    foo2<Baz>(bar); // Fails 
} 

foo1 साथ कोई परेशानी नहीं है, जो bar के प्रकार स्पष्ट रूप से निर्दिष्ट करता है नहीं है।

main.cpp:19:5: fatal error: no matching function for call to 'foo2' 
    foo2<Baz>(bar); // Fails 
    ^~~~~~~~~ 
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D' 
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {} 
    ^
1 error generated. 

यह मेरी समझ है कि सी ++ अतिभारित समारोह के पते का समाधान नहीं कर सकते हैं और करते हैं:

हालांकि, foo2 है, जो सभी लेकिन bar का एक संस्करण के लिए SFINAE के माध्यम से ही अक्षम करें, निम्न संदेश के साथ संकलित करने के लिए विफल रहता है एक ही समय में टेम्पलेट तर्क कटौती।

क्या इसका कारण है? foo2<Baz>(bar); (या कुछ समान) बनाने के लिए कोई तरीका है संकलित?

+2

संकलक केवल 'bar' के खिलाफ' डी * '' bar' के अन्य भार के को छान के लिए, तुलना क्या वास्तविक कटौती में '' के खिलाफ डी * तुलना करने के लिए बाद में कार्य करते निर्णय लेने की एक उप-प्रक्रिया के रूप में प्रक्रिया। यह इस ट्रेल उप-प्रक्रिया के परिणामों को फ़ंक्शन के शेष हिस्सों में बदलने की कोशिश नहीं करेगा क्योंकि यह वास्तविक कटौती में ही होता है। –

+0

तो सवाल यह है कि "विशिष्ट फ़ंक्शन अधिभार के प्रकार को कैसे घटाया जाए?" या एक साधारण 'टेम्पलेट शून्य foo2 (शून्य (*) (टी *)) {}' पर्याप्त है? –

+0

@MykolaBogdiuk व्यावहारिक मामला किसी भी फ़ंक्शन का समर्थन करना है जिसे 'टी * '(जो भी इसके वापसी प्रकार, उदाहरण के लिए) कहा जा सकता है, लेकिन सामान्य उत्तर अब मुझे रूचि देता है। – Quentin

उत्तर

0

सामान्य जवाब के कुछ प्रकार यहाँ है: Expression SFINAE to overload on type of passed function pointer

व्यावहारिक मामले के लिए, वहाँ प्रकार लक्षण या decltype() उपयोग करने की आवश्यकता है - अच्छे पुराने अधिभार संकल्प आप के लिए सबसे उपयुक्त समारोह का चयन करें और इसे तोड़ने में होगा 'तर्क' और 'वापसी प्रकार'। बस, हर संभव बुला सम्मेलनों

// Common functions 
template <class T, typename R> void foo2(R(*)(T*)) {} 

// Different calling conventions 
#ifdef _W64 
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {} 
#else 
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {} 
#endif 

// Lambdas 
template <class T, class D> 
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {} 

यह उन्हें एक टेम्प्लेटेड संरचना

template<typename... T> 
struct Foo2 { 
    // Common functions 
    template <typename R> static void foo2(R(*)(T*...)) {} 
    ... 
}; 
Zoo2<Baz>::foo2(bar); 

हालांकि में रैप करने के लिए उपयोगी हो सकता है की गणना के रूप में वे संशोधक (const, volatile है यह सदस्य कार्यों के लिए अधिक कोड की आवश्यकता होगी, &&)

1

टिप्पणियों में उल्लिखित अनुसार, [14.8.2.1/6] (कार्य ड्राफ्ट, फ़ंक्शन कॉल से टेम्पलेट तर्क को कम करना) इस मामले (जोर खान में नियम):

जब पी एक समारोह का प्रकार, समारोह सूचक प्रकार, या सूचक सदस्य समारोह प्रकार के है:

  • तर्क है एक अधिभार सेट जिसमें एक या अधिक फ़ंक्शन टेम्पलेट होते हैं, पैरामीटर को गैर-कटौती संदर्भ के रूप में माना जाता है।

  • तर्क, परीक्षण तर्क कटौती सेट के सदस्यों में से प्रत्येक का उपयोग कर प्रयास किया गया एक अधिभार सेट (समारोह टेम्पलेट्स युक्त नहीं) है। यदि कटौती केवल ओवरलोड सेट सदस्यों में से एक के लिए सफल होती है, तो उस सदस्य को कटौती के लिए तर्क मान के रूप में उपयोग किया जाता है। यदि ओवरलोड लोड के एक से अधिक सदस्यों के लिए कटौती सफल होती है तो पैरामीटर को गैर-कटौती संदर्भ के रूप में माना जाता है।

SFINAE खेल के लिए अपने भाग लेता है एक बार कटौती खत्म हो गया है, तो यह मानक के नियमों के आसपास काम करने के लिए मदद नहीं करता है।
अधिक जानकारी के लिए, आप ऊपर दिए गए बुलेट के अंत में उदाहरण देख सकते हैं।

अपने आखिरी सवाल के बारे में:

वहाँ foo2<Baz>(bar); (या कुछ इसी तरह) बनाने के लिए एक रास्ता है संकलन?

दो संभव विकल्पों:

  • आप foo2 की परिभाषा को संशोधित नहीं करना चाहते हैं, तो आप इसे के रूप में आह्वान कर सकते हैं:

    foo2<Baz>(static_cast<void(*)(Baz *)>(bar)); 
    

    इस तरह आप स्पष्ट रूप से एक समारोह लेने अधिभार सेट से बाहर।

  • तो संशोधित foo2 अनुमति दी है, तो आप इसे के रूप में फिर से लिखने कर सकते हैं:

    template <class T, class R> 
    auto foo2(R(*d)(T*)) {} 
    

    यह कम या ज्यादा क्या आप पहले था है, इस मामले और एक वापसी प्रकार आप स्वतंत्र रूप से अनदेखा कर सकते हैं में कोई decltype
    असल में आपको ऐसा करने के लिए किसी भी SFINAE'd फ़ंक्शन का उपयोग करने की आवश्यकता नहीं है, कटौती पर्याप्त है।
    इस मामले में foo2<Baz>(bar); सही ढंग से हल किया गया है।

संबंधित मुद्दे