2016-01-15 11 views
7

हाल ही में, मुझे लैम्ब्डा अभिव्यक्ति के पैरामीटर के रूप में पारित फ़ंक्शन को पास करने के साथ एक अजीब समस्या का सामना करना पड़ा। यह कोड क्लैंग 3.5+ के साथ ठीक है, लेकिन जी ++ 5.3 के साथ असफल रहा है और मुझे आश्चर्य है कि समस्या सी ++ मानक में है, क्लैंग में एक गैर मानक विस्तार, या जीसीसी में एक अवैध वाक्य रचनात्मक व्याख्या।सी ++ 11 लैम्ब्डा पैरामीटर के रूप में पासिंग फ़ंक्शन

error: variable ‘fn’ has function type 
      { promise->set_value(fn(std::move(args)...)); }; 
(...) 
error: field ‘async(Fn&&, Args&& ...) [with Fn = int (&)(int, int); Args = {int&, int&}; T = int]::<lambda()>::<fn capture>’ invalidly declared function type 
     auto lambda = [promise, fn, args...](void) 

सौभाग्य से, मैं एक साधारण वैकल्पिक हल मिल गया है, एक std :: समारोह वस्तु जोड़ने, समारोह पैरामीटर encapsulating:

template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type> 
    std::future<T> 
    async(Fn &&fn, Args &&... args) 
    { 
     std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>(); 
     auto lambda = [promise, fn, args...](void) 
      { promise->set_value(fn(std::move(args)...)); }; 
     send_message(std::make_shared<post_call>(lambda)); 
     return promise->get_future(); 
    }; 

जीसीसी निम्नलिखित सूचना:

उदाहरण कोड

परी सरल है :

template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type> 
    std::future<T> 
    async(Fn &&fn, Args &&... args) 
    { 
     std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>(); 
     std::function<T(typename std::remove_reference<Args>::type...)> floc = fn; 
     auto lambda = [promise, floc, args...](void) 
      { promise->set_value(floc(std::move(args)...)); }; 
     send_message(std::make_shared<post_call>(lambda)); 
     return promise->get_future(); 
    }; 

हालांकि मैं पूरी तरह से समझ नहीं पा रहा हूं कि एफआईआर में क्या गलत था टी कोड का टुकड़ा, जो सफलतापूर्वक clang के साथ संकलित और त्रुटियों के बिना चलाने के लिए।


संपादित

मैं सिर्फ देखा है, कि मेरे समाधान भयंकर रूप में विफल रहता है, यदि तर्क में से एक के लिए एक संदर्भ होना चाहिए था। तो यदि आपके पास कोई अन्य सुझाव है जो सी ++ 11 के साथ काम कर सकता है (यानी विशेष लैम्ब्डा कैप्चर [सी ++ 14 फीचर] के बिना), यह वास्तव में अच्छा होगा ...

+0

अगर आप 'पारित ऑटो p_fn क्या होता है = और lambda के लिए fn'? –

+0

@ जोएल कॉर्नेट सिगसेग या कुछ इसी तरह। एक संदर्भ के रूप में 'fn' गुजरने के साथ वही चीज़ खुश है। हालांकि यह दोनों मामलों में संकलित करता है। – Marandil

उत्तर

5

परिवर्तनीय fn में फ़ंक्शन प्रकार है। विशेष रूप से, इसमें Fn = int (&)(int, int) टाइप किया गया है।

Fn (संदर्भ नहीं) का मान int(int,int) प्रकार का है।

यह मान संग्रहीत नहीं किया जा सकता है।

मुझे आश्चर्य है कि यह आपके लिए ऑटो-क्षय नहीं करेगा। सी ++ 14 में, आप कर सकते हैं:

auto lambda = [promise, fn=fn, args...](void) 
     { promise->set_value(fn(std::move(args)...)); }; 

जो आंतरिक रूप से fn के fn (बाह्य) प्रकार क्षय चाहिए। यदि यह काम नहीं करता है:

auto lambda = [promise, fn=std::decay_t<Fn>(fn), args...](void) 
     { promise->set_value(fn(std::move(args)...)); }; 

जो स्पष्ट रूप से इसे अस्वीकार करता है। (क्षय एक ऑपरेशन है जो भंडारण के लिए उपयुक्त प्रकार बनाता है)।

दूसरे, आप mutable जोड़ना चाहिए:

auto lambda = [promise, fn=std::decay_t<Fn>(fn), args...](void)mutable 
     { promise->set_value(fn(std::move(args)...)); }; 

या std::move ज्यादा कुछ नहीं होगा। (एक कॉन्स वैल्यू लेना ज्यादा नहीं करता है)।

तीसरा, यदि आप एक अनावश्यक बनाने के बजाय में वादा स्थानांतरित कर सकते हैं ptr साझा:

template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type> 
std::future<T> 
async(Fn &&fn, Args &&... args) 
{ 
    std::promise<T> promise; 
    std::future<T> ret = promise.get_future(); 
    auto lambda = 
     [promise=std::move(promise), fn=std::decay_t<Fn>(fn), args...] 
    () mutable { 
     promise.set_value(fn(std::move(args)...)); 
     }; 
    send_message(std::make_shared<post_call>(lambda)); 
    return ret; 
}; 

इस मान लिया जाता है अपने post_call वर्ग के लिए कदम-केवल lambdas संभाल कर सकते हैं (अगर यह एक std::function यह नहीं कर सकता है)।

+0

वाह, सलाह के लिए बहुत धन्यवाद, लेकिन दुर्भाग्यवश उनमें से अधिकांश को सी ++ 14 की आवश्यकता है, और मैंने परियोजना के लिए सी ++ 11 तक चिपकने का फैसला किया है (दोनों कंपाइलर प्रारंभिक लैम्ब्डा का उपयोग करते समय C++ 14 एक्सटेंशन का उपयोग करने के बारे में शिकायत करते हैं कैप्चर करता है, जबकि मैं शुद्ध सी ++ 11 तक चिपकने की कोशिश कर रहा हूं), इसलिए वादा एक सूचक होना चाहिए (हालांकि मुझे लगता है कि यह सिर्फ सामान्य सूचक हो सकता है, लेकिन मुझे यकीन नहीं है कि दोनों मामलों में क्या होने जा रहा है, जब लैम्ब्डा कभी नहीं चल रहा है ...)। निश्चित रूप से आपकी सलाह का उपयोग करेगा, एक बार जब मैं सी ++ 1y के साथ कुछ लिखने वाला हूं;) – Marandil

+2

@ मरंडिल मूव-इन लैम्बडास को मैन्युअल रूप से सहायक प्रकार के रूप में लिखा जा सकता है (यह थोड़ा वर्बोज़ है, मैं स्वीकार करता हूं)। आप 'auto my_fn = std :: आगे (fn); ',' फिर 'my_fn' पर कब्जा' के साथ स्थानीय रूप से' fn' की एक प्रति संग्रहीत करके क्षय समस्या को हल कर सकते हैं। ध्यान दें कि ऊपर मेरा सी ++ 14 संस्करण (और आपका) लम्बडा में 'तर्क ...' की प्रतिलिपि बनाता है: यह अक्षम है। – Yakk

0

आपका पहला कोड VS2015 के तहत ठीक संकलित, तो मैं आपकी समस्या को पुन: पेश नहीं कर सकता लेकिन जब से तुम सार्वभौमिक संदर्भों का उपयोग कर रहे हैं, मैं की तरह कुछ की कोशिश करेंगे:

template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type> 
std::future<T> async2(Fn &&fn, Args &&... args) 
{ 
    std::promise<T> prom; 
    auto floc = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...); 
    auto lambda = [&prom, floc](void) 
    { prom.set_value(floc()); }; 
    send_message(std::make_shared<post_call>(lambda)); 
    return prom.get_future(); 
}; 
संबंधित मुद्दे