2017-01-29 17 views
18

निम्न कोड संकलित क्यों नहीं करता है (सी ++ 11 मोड में)?टेम्पलेट फ़ंक्शन पैरामीटर के रूप में लैम्ब्डा पास करें

#include <vector> 

template<typename From, typename To> 
void qux(const std::vector<From>&, To (&)(const From&)) { } 

struct T { }; 

void foo(const std::vector<T>& ts) { 
    qux(ts, [](const T&) { return 42; }); 
} 

त्रुटि संदेश है:

prog.cc:9:5: error: no matching function for call to 'qux' 
    qux(ts, [](const T&) { return 42; }); 
    ^~~ 
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)' 
void qux(const std::vector<From>&, To (&)(const From&)) { } 
    ^

लेकिन यह स्पष्ट नहीं होता कि ऐसा क्यों पैरामीटर से मेल नहीं हो सकता है।

अगर मैं qux एक गैर टेम्पलेट कार्य करना, T और Toint साथ साथ From की जगह, यह संकलित करता है।

+2

सबसे पहले 'qux' कुछ भी वापस नहीं कर रहा है, इसलिए इसे' ऑटो बार = qux ... ' – nbro

+0

@nbro संकलित नहीं करना चाहिए, जिसके पास समस्या से कोई लेना देना नहीं है। – emlai

+2

यदि आप इतना जानते हैं तो आप एक प्रश्न नहीं पूछेंगे। वैसे भी, ऊपर उठाया क्योंकि मैं भी नहीं जानता क्यों। – nbro

उत्तर

13

एक लैम्ब्डा फ़ंक्शन सामान्य कार्य नहीं है। प्रत्येक लैम्ब्डा का अपना प्रकार होता है जो किसी भी मामले मेंTo (&)(const From&) नहीं है।
एक गैर कैप्चरिंग लैम्ब्डा का उपयोग कर अपने मामले में To (*)(const From&) को क्षय कर सकते हैं:

#include <vector> 

template<typename From, typename To> 
void qux(const std::vector<From>&, To (&)(const From&)) { } 

struct T { }; 

void foo(const std::vector<T>& ts) { 
    qux(ts, *+[](const T&) { return 42; }); 
} 

int main() {} 
:

qux(ts, +[](const T&) { return 42; }); 

टिप्पणी में बताया गया है, सबसे अच्छा आप एक लैम्ब्डा से बाहर निकलने के लिए क्या कर सकते हैं यह है


नोट: मुझे लगता है कि वास्तविक समस्या के लिए रिटर्न प्रकार और तर्कों के प्रकार को कम करना अनिवार्य है। अन्यथा आप पूरे लैम्ब्डा को सामान्य कॉल करने योग्य ऑब्जेक्ट के रूप में आसानी से कम कर सकते हैं और इसे सीधे उपयोग कर सकते हैं, कुछ भी क्षीण करने की आवश्यकता नहीं है।

+1

धन्यवाद! '* +' के साथ लैम्ब्डा को उपसर्ग करने से मुझे इसे रेफरी से पास करने की अनुमति मिलती है। सी ++ से प्यार होना चाहिए ... – emlai

+0

@tuple_cat अच्छी पकड़, मैं जवाब पर आपकी टिप्पणी जोड़ दूंगा। – skypjack

+2

दरअसल, केवल '*' पर्याप्त है, '* +' की आवश्यकता नहीं है। – emlai

10

आप निष्कर्ष निकाला To प्रकार का उपयोग करने की जरूरत नहीं है, तो आप बस पूरे पैरामीटर के प्रकार यह मान सकते हैं:

template<typename From, typename F> 
void qux(const std::vector<From>&, const F&) { } 
+0

हां, यह अनुशंसित दृष्टिकोण है। – milleniumbug

+3

'std :: result_of' – milleniumbug

+1

यह * * +' के साथ सिंटैक्स निंजा जाने से बहुत बेहतर है ... –

6

मुझे सही अगर मैं गलत हूँ, लेकिन टेम्प्लेट पैरामीटर कटौती केवल सटीक प्रकार deduces संभावित रूपांतरणों पर विचार किए बिना।

नतीजतन संकलक To (&)(const From&) के लिए To और From यह मान सकते हैं नहीं है, क्योंकि qux समारोह के लिए एक संदर्भ की उम्मीद है, लेकिन आप एक लैम्ब्डा जो अपनी ही प्रकार है प्रदान करते हैं।

2

आपने To पर अनुमान लगाने के लिए संकलक को बिल्कुल कोई मौका नहीं छोड़ा है। इस प्रकार, आपको इसे स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता है।

इसके अलावा, यहां लैम्ब्डा को पॉइंटर द्वारा पारित करने की आवश्यकता है।

अंत में, इस संस्करण ठीक संकलित:

template<typename From, typename To> 
void qux(const std::vector<From>&, To (*)(const From&)) { } 

struct T { }; 

void foo(const std::vector<T>& ts) { 
    qux<T,int>(ts,[](const T&) { return 42; }); 
} 
2

आप दोनों अंतर्निहित प्रकार रूपांतरण उम्मीद कर रहे हैं और ऐसा करने के लिए टेम्पलेट प्रकार कटौती (अनाम समारोह ऑब्जेक्ट प्रकार से संदर्भ प्रकार कार्य करने के लिए)। हालांकि, you can't have both, क्योंकि आपको उपयुक्त रूपांतरण अनुक्रम खोजने के लिए लक्ष्य प्रकार जानने की आवश्यकता है।

1

लेकिन यह समझा नहीं गया कि यह पैरामीटर से क्यों मेल नहीं खा सकता है।

टेम्पलेट कटौती बिल्कुल प्रकारों से मेल खाने का प्रयास करती है। यदि प्रकारों को कम नहीं किया जा सकता है, तो कटौती विफल हो जाती है। रूपांतरण कभी नहीं माना जाता है।

इस एक्सप्रेशन में:

qux(ts, [](const T&) { return 42; }); 

लैम्ब्डा अभिव्यक्ति के प्रकार के कुछ अद्वितीय, अनाम प्रकार है। जो भी प्रकार है, यह निश्चित रूप से To(const From&) नहीं है - इसलिए कटौती विफल हो जाती है।


अगर मैं qux एक गैर टेम्पलेट कार्य करना, T और Toint साथ साथ From की जगह, यह संकलित करता है।

यह सच नहीं है। हालांकि, अगर तर्क पॉइंटरसंदर्भ के बजाय कार्य करने के लिए कार्य करने के लिए किया गया था, तो यह होगा। ऐसा इसलिए है क्योंकि कोई कैप्चर वाला लैम्ब्डा समकक्ष फ़ंक्शन पॉइंटर प्रकार के लिए पूरी तरह से परिवर्तनीय है। कटौती के संदर्भ के बाहर इस रूपांतरण की अनुमति है।

template <class From, class To> 
void func_tmpl(From(*)(To)) { } 

void func_normal(int(*)(int)) { } 

func_tmpl([](int i){return i; }); // error 
func_tmpl(+[](int i){return i; }); // ok, we force the conversion ourselves, 
            // the type of this expression can be deduced 
func_normal([](int i){return i; }); // ok, implicit conversion 

यह वही कारण है कि यह विफल हो जाता है:

template <class T> void foo(std::function<T()>); 
foo([]{ return 42; }); // error, this lambda is NOT a function<T()> 

लेकिन यह सफल होता है:

void bar(std::function<int()>); 
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()> 

पसंदीदा दृष्टिकोण प्रतिदेय के प्रकार निकालना होगा और std::result_of का उपयोग करके परिणाम निकालें:

template <class From, 
    class F&&, 
    class To = std::result_of_t<F&&(From const&)>> 
void qux(std::vector<From> const&, F&&); 

अब आप अपना लैम्ब्डा, या फ़ंक्शन, या फ़ंक्शन ऑब्जेक्ट बस ठीक कर सकते हैं।

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

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