2017-02-20 14 views
5

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

Here's the overloaded version -

#include <iostream> 
using namespace std; 


void doSome(int (*func)(int &)){ 
    int a; 
    a = 5; 
    int res = func(a); 
    cout << a << "\n"; 
} 


void doSome(int (*func)(double &)){ 
    double a; 
    a = 5.2; 
    int res = func(a); 
    cout << a << "\n"; 
} 


int main() { 
    doSome([](int &a){ 
     a += 2; 
     return 1; 
    }); 

    doSome([](double &a){ 
     a += 2.5; 
     return 1; 
    }); 
    return 0; 
} 

ध्यान दें कि मैं सरलीकरण के लिए int और double का उदाहरण लिया है, वे वास्तविक कोड में कुछ पूरी तरह से अलग (और जटिल) प्रकार हो सकता है।


Here's what I've tried yet -

#include <iostream> 
using namespace std; 

template <typename F, typename S> 
void doSome(F &func){ 
    S a; 
    auto res = func(a); 
    cout << res << "\n"; 
} 

template<> 
void doSome<typename F, int> (F &func){ 
    int a; 
    a = 5; 
    auto res = func(a); 
    cout << res << "\n"; 
} 

template<> 
void dpSome<typename F, double> (F &func){ 
    double a; 
    a = 5.5 
    auto res = func(a); 
    cout << res << "\n"; 
} 


int main() { 
    doSome([](int &a){ 
     a += 2; 
     return 1; 
    }); 

    doSome([](double &a){ 
     a += 2.5; 
     return 1; 
    }); 
    return 0; 
} 

टेम्प्लेट काम करता है, लागू भी है, जबकि अगर मैं समारोह के लिए <any type hints> पारित करने के लिए नहीं है, कि ज्यादा बेहतर समाधान होगा।

+1

'टेम्पलेट <> शून्य doSome (एफ एंड समारोह)' गलत है, और यहां तक ​​कि अगर आप 'टेम्पलेट शून्य doSome (एफ एंड समारोह)', आप आंशिक रूप से विशेषज्ञ नहीं कर सकते समारोह में लिखा है टेम्पलेट्स – xinaiz

+0

मैं सिर्फ उत्सुक हूं, आप ओवरलोडिंग से टेम्पलेट विशेषज्ञता में क्यों परिवर्तित करना चाहते हैं? अधिभार आमतौर पर फ़ंक्शन टेम्पलेट विशेषज्ञता से बेहतर और कम आश्चर्यजनक होता है। ओवरलोडिंग यही कारण है कि आपको फ़ंक्शन टेम्पलेट्स के लिए आंशिक विशेषज्ञता की आवश्यकता नहीं है। किसी के लिए मेरी सलाह जहां भी संभव हो फंक्शन टेम्पलेट्स विशेषज्ञता से बचने के लिए होगा। क्या यह संभव है कि यह एक एक्सवाई समस्या है? –

+1

@NirFriedman फ़ंक्शन पॉइंटर्स केवल गैर-कैप्चरिंग लैम्बडास के लिए काम करते हैं, और यह बहुत सीमित है। 'std :: function' ने इसे हल कर लिया होगा और मैंने इसे हर जगह इस्तेमाल किया है लेकिन ये फ़ंक्शंस प्रदर्शन महत्वपूर्ण हैं और बेंचमार्क ने हमारे मामले में फ़ंक्शन पॉइंटर्स को बहुत तेज़ बताया है, इसलिए .. –

उत्तर

5

आपके दृष्टिकोण के साथ कुछ समस्याएं हैं। सबसे पहले, आप आंशिक रूप से फ़ंक्शन टेम्पलेट्स का विशेषज्ञ नहीं हो सकते हैं, इसलिए यह गेट से बाहर है। दूसरा, आप अपना कार्य lvalue संदर्भ से ले रहे हैं - जो आपको लैम्ब्डा में गुजरने से रोकता है, जो एक प्रकोप है।


इस मामले में, यह सिर्फ अपने कार्य टेम्पलेट पर कुछ SFINAE जोड़ना आसान है ताकि एक ही अधिभार संकल्प में भाग लेता है अगर यह int& साथ कहा जा सकता है और केवल double& के साथ अन्य:

template <class F> 
auto doSome(F f) 
    -> decltype(f(std::declval<int&>()), void()) 
{ 
    // int& case 
}   

template <class F> 
auto doSome(F f) 
    -> decltype(f(std::declval<double&>()), void()) 
{ 
    // double& case 
}   
+0

क्या आप' - > decltype (एफ (std :: declval ()), शून्य()) '? यह यहाँ कैसे काम कर रहा है? '->' का मतलब है कि वापसी मूल्य अभिव्यक्ति आगे नहीं है, और दोनों विधियों के पास वापसी मूल्य के रूप में int है और यह 'शून्य()' क्या कर रहा है! –

+0

@ अहिनावगुनियल [अभिव्यक्ति SFINAE] (http: // stackoverflow।कॉम/क्यू/12654067/206 9 064) – Barry

+0

मैंने उस उत्तर को पढ़ लिया और इसके बारे में समझ लिया। फिर भी मैं समझ नहीं पा रहा था कि [decltype] (http://en.cppreference.com/w/cpp/language/decltype) के बाद से 'शून्य()' क्या कर रहा है, दो पैराम नहीं लेता है, या यह वाक्यविन्यास है अभिव्यक्ति SFINAE लागू करने के लिए? –

1

यदि आप doSome() का एक सामान्य संस्करण बनाना चाहते हैं, जो ओवरलोड रिज़ॉल्यूशन के लिए SFINAE का उपयोग नहीं करता है, तो यह थोड़ा अधिक जटिल हो जाता है।

#include <type_traits> // For std::remove_reference_t. 

namespace detail { 
    // Helper to isolate return and parameter types, for a single-parameter callable. 
    template<typename T> 
    struct isolate_types; 

    // Function. 
    template<typename R, typename P> 
    struct isolate_types<R(P)>    { using Ret = R; using Param = P; }; 

    // Function pointer. 
    template<typename R, typename P> 
    struct isolate_types<R(*)(P)>   { using Ret = R; using Param = P; } 

    // Pointer-to-member-function. Used for lambdas & functors. 
    // Assumes const this pointer. 
    template<typename R, typename C, typename P> 
    struct isolate_types<R (C::*)(P) const> { using Ret = R; using Param = P; }; 

    // Lambda. Uses lambda's operator(). 
    // Credit goes to ecatmur: http://stackoverflow.com/a/13359520/5386374 
    template<typename T> 
    struct isolate_types : isolate_types<decltype(&std::remove_reference_t<T>::operator())> {}; 

    // Individual type aliases. 
    template<typename T> 
    using IsolateReturn = typename isolate_types<T>::Ret; 
    template<typename T> 
    using IsolateParam = typename isolate_types<T>::Param; 

    // Internal values, used by doSome(). 
    template<typename T> T value; 

    template<> constexpr int value<int> = 5; 
    template<> constexpr double value<double> = 5.2; 
    // Define others as needed... 
} // namespace detail 

template<typename F> 
void doSome(F func) { 
    // Determine necessary types. 
    using Ret = detail::IsolateReturn<F>; 
    using Param = std::remove_reference_t<detail::IsolateParam<F>>; 

    // And voila. 
    Param a = detail::value<Param>; 
    Ret res = func(a); // Can also use auto, if Ret isn't needed elsewhere. 
    std::cout << a << "\n"; 
} 

इसे अपने कोड में प्लग करना ... and it works


ध्यान दें कि मुझे यकीन है कि के रूप में लिखा है कि अगर यह सब lambdas के साथ काम करेंगे नहीं कर रहा हूँ, और यह वर्तमान कार्यों के लिए संदर्भ के साथ काम नहीं कर सकते हैं। हालांकि, isolate_types के अतिरिक्त विशेषज्ञता जोड़कर, विस्तार करना काफी आसान है।

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