2015-09-13 18 views
5

मैं टेम्पलेट फ़ंक्शन बनाने की कोशिश कर रहा हूं जिसमें किसी अन्य प्रकार को पैरामीटर की संख्या और पैरामीटर के साथ पास करना संभव है और इसे std::function पर बाध्य करें। मैं यह कर में कामयाब रहे:वैराडिक टेम्पलेट्स, टाइप कटौती और std :: function

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return bar; 
} 

template <typename Ret, typename... Args> 
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f) 
{ 
    return f; 
} 

int main() 
{ 
    //auto barp = func(foo); // compilation error 
    auto bar = func(std::function<void (int)>(foo)); 

    bar (0); // prints 0 
} 

मैं सिर्फ auto barp = func(foo); फोन और प्रकार निष्कर्ष निकाला है, लेकिन इस लाइन निम्नलिखित संकलन त्रुटियों देता है चाहते हैं:

error: no matching function for call to ‘func(void (&)(int))’ 
    auto barp = func(foo); 
         ^
note: candidate is: 
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>) 
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f) 
          ^
note: template argument deduction/substitution failed: 
note: mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’ 
    auto barp = func(foo); 
        ^

क्यों इसके साथ std::function<_Res(_ArgTypes ...)> मिलान करने के लिए कोशिश कर रहा है int (*)(int)? मुझे लगता है कि मुझे कंपाइलर को _Res(_ArgTypes ...) से int(int) तक विस्तारित करना चाहिए, लेकिन कैसे?

+4

क्यों आप सब पर एक 'एसटीडी करने के लिए परिवर्तित कर रहे हैं :: function'? 'std :: function' एक प्रकार का एरर क्लास है: यह किस प्रकार से बनाया गया है, इसकी टाइप जानकारी लेता है, और इसमें से अधिकांश को मिटा देता है। टाइप कटौती इसकी तर्क लेती है, और इसके प्रकार को घटाती है, और कोड उत्पन्न करती है। आप कम करने के लिए कह रहे हैं कि किस प्रकार से कुछ मिटाना है। यह कवच का एक सूट बनाने जैसा है जो पहनने वाले को गोली मारने के लिए बंदूक बनाता है; टाइप इरेशन अधिकांश इंद्रियों में * विपरीत * प्रकार की कटौती है। यह बहुत ही कम विचार है। यह संभव है, लेकिन सी ++ के पास इसे आसान बनाने का कोई कारण नहीं है * आसान * क्या आपके पास व्यावहारिक उपयोग का मामला है? – Yakk

+0

अधिभार, या परिवर्तनीय तर्क सूचियों के बारे में क्या? केवल कुछ परिस्थितियों में यह संभव है। हालांकि आप बूस्ट function_traits जैसे चीजों का उपयोग कर सकते हैं। – tahsmith

+0

@Yakk, मैं एक "ज्ञापन" फ़ंक्शन को कोड करने का प्रयास कर रहा था, इस अर्थ में कि यह उसी पैरामीटर के साथ एक कॉल करने योग्य लौटाएगा और इसके तर्क का रिटर्न प्रकार होगा जो मूल फ़ंक्शन को कॉल करेगा, सिवाय इसके कि यह पिछले गणना वाले मानों को देखेगा (एक उपयुक्त मानचित्र में संग्रहीत करने के लिए)। मैं मूल फ़ंक्शन को संग्रहीत करने के लिए 'std :: function' का उपयोग करूंगा। यकीन नहीं है कि यह संभव है हालांकि। – Tarc

उत्तर

2

एक फ़ंक्शन std::function नहीं है, यह एक में परिवर्तनीय है। हालांकि, आप किसी फ़ंक्शन के तर्कों को कम कर सकते हैं, हालांकि, ओवरलोड के बारे में अस्पष्टता को छोड़कर।

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return 0; 
} 

// Will cause error. 
//int foo(double); 

template <typename Ret, typename... Args> 
std::function<Ret (Args...)> func(Ret f(Args...)) 
{ 
    return f; 
} 

int main() 
{ 
    auto bar = func(foo); 
    bar (0); // prints 0 
} 

क्या आप मूल std::function के साथ क्या करना चाहते हैं तो इस के समान है, जो और अधिक स्पष्ट रूप से काम नहीं करता है:

template<typename T> 
struct A 
{ 
    A(T); 
}; 

template<typename T> 
void func(A<T> a); 

int main() 
{ 
    func(42); 
} 

42 एक A यह, हालांकि एक के लिए परिवर्तित किया जा सकता नहीं है,। हालांकि, इसे किसी एक में परिवर्तित करने के लिए पहले से ही ज्ञात होने के लिए T की आवश्यकता होगी।

+0

'और foo' थोड़ा अपरिवर्तनीय है। एक कॉलिंग अभिव्यक्ति के बाहर एक फ़ंक्शन नाम को सी/सी ++ में फ़ंक्शन पॉइंटर में स्पष्ट रूप से परिवर्तित किया जाता है। – CoffeeandCode

+0

@CoffeeandCode, हाँ इसे बदल दिया। मैं इसे सामान्य रूप से रखना पसंद करता हूं, क्योंकि सदस्य कार्यों के लिए इसकी आवश्यकता होती है। लेकिन यहां यह सिर्फ एक अतिरिक्त जटिलता है। – tahsmith

1

आपका कोड शब्दार्थ के बराबर है (अगर यह संकलित) इस:

int foo(int x){ 
    std::cout << x << std::endl; 
    return x; 
} 

int main(){ 
    auto bar = [](int x){ return foo(x); }; 
    bar(0); 
} 

अलावा return x हिस्से से है, लेकिन वह सिर्फ मुझे अपने अपरिभाषित व्यवहार को सही है।

std::function लौटने के लिए आपका कार्य बेहद अनावश्यक है, शायद कम टाइपिंग के लिए छोड़कर।

आप रैपर फ़ंक्शन के बिना आसानी से std::function एस कन्स्ट्रक्टर का उपयोग कर सकते हैं।

लेकिन, अभी भी।

ऐसा करने के लिए जो आप करना चाहते हैं, आपको फ़ंक्शन पॉइंटर को ही पास करना चाहिए; असंभव रूपांतरण के बिना।

इस प्रयास करें:

int foo(int x){ 
    return x + 1; 
} 

template<typename Ret, typename ... Args> 
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{ 
    return {fp}; 
} 

int main(){ 
    auto bar = func(foo); 
    std::cout << bar(0) << std::endl; // outputs '1' 
} 

कारण अपने कोड काम नहीं करता अंतर्निहित जगह लेने के लिए जब आप func लिए एक तर्क पारित कोशिश कर रूपांतरण के कारण है।

जैसा कि मैंने कहा था, वर्तमान में आपका कोड लैम्बडा अभिव्यक्तियों का उपयोग करके ऊपर दिखाए गए उदाहरण के बराबर है। फ़ंक्शन रैपिंग की आवश्यकता होने पर मैं केवल लैम्ब्डा अभिव्यक्तियों का उपयोग करने की अनुशंसा करता हूं! वे अधिक लचीला हैं और लाइब्रेरी सुविधा के बजाए भाषा का मुख्य हिस्सा हैं।

याद रखें, गैर-कैप्चरिंग लैम्बडा फ़ंक्शन पॉइंटर्स में परिवर्तनीय हैं; तो निम्नलिखित अनुरूप है:

int main(){ 
    auto func = +[](int x){ return x + 1; }; 

    std::cout << "foo(5) = " << func(5) << std::endl; 

    func = [](int x){ return x * 2; }; 

    std::cout << "bar(5) = " << func(5) << std::endl; 
} 

सूचना हम समारोह सूचक घोषणाओं के साथ चारों ओर गंदगी नहीं है:

int main(){ 
    int(*bar)(int) = [](int x){ return x + 1; }; 
    std::cout << bar(0) << std::endl; 
} 

और इसी तरह की सुविधा के लिए के रूप में आप अपनी पोस्ट में चाहते हैं, हम कुछ इस तरह लिख सकते हैं या पुस्तकालय के प्रकार? सभी को पढ़ने/लिखने के लिए बहुत अच्छा है। One thing to notice in this example is the unary + operator; to perform a conversion to function pointer before assigning it to the variable. यह वास्तव में बहुत कार्यात्मक लगता है, जो कि आप यहां प्राप्त करने की कोशिश कर रहे हैं।

1

अपने operator() के माध्यम से functor (लैम्ब्डा और std :: समारोह) खोलने में प्रयास करें:

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return 0; 
} 

template<typename /*Fn*/> 
struct function_maker; 

template<typename RTy, typename... ATy> 
struct function_maker<RTy(ATy...)> 
{ 
    template<typename T> 
    static std::function<RTy(ATy...)> make_function(T&& fn) 
    { 
     return std::function<RTy(ATy...)>(std::forward<T>(fn)); 
    } 
}; 

template<typename /*Fn*/> 
struct unwrap; 

template<typename CTy, typename RTy, typename... ATy> 
struct unwrap<RTy(CTy::*)(ATy...) const> 
    : function_maker<RTy(ATy...)> { }; 

template<typename CTy, typename RTy, typename... ATy> 
struct unwrap<RTy(CTy::*)(ATy...)> 
    : function_maker<RTy(ATy...)> { }; 

template<typename T> 
auto func(T f) 
    -> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>())) 
{ 
    return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f)); 
} 

int main() 
{ 
    //auto barp = func(foo); // compilation error 
    auto bar = func(std::function<void(int)>(foo)); 

    auto bar2 = func([](int) 
    { 
     // ... 
    }); 

    bar(0); // prints 0 
} 

Demo

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