2017-08-06 9 views
15

मैं std::variant, lambdas और std::future के साथ खेल रहा हूं, और जब मैंने उन्हें एक साथ लिखने की कोशिश की तो सुपर अजीब परिणाम प्राप्त हुए। यहाँ उदाहरण हैं:,विभिन्न लैम्ब्डा अभिव्यक्तियों के साथ std :: संस्करण को प्रारंभ नहीं कर सकता

Error C2665 'std::variant<std::function<std::future<void> (int)>,std::function<void (int)>>::variant': none of the 2 overloads could convert all the argument types

ठीक लौटने voidint करने से परिवर्तन variant के आइटम हस्ताक्षर करने देता है::

using variant_t = std::variant< 
    std::function<std::future<void>(int)>, 
    std::function<void(int)> 
>; 
auto f1 = [](int) { return std::async([] { return 1; }); }; 
auto f2 = [](int) { return std::async([] { }); }; 

variant_t v1(std::move(f1)); // !!! why DOES this one compile when it SHOULDN'T? 
auto idx1 = v1.index(); //equals 1. WHY? 

variant_t v2(std::move(f2)); // !!! why DOESN'T this one compile when it SHOULD? 

यहाँ संकलन त्रुटि है

using variant_t = std::variant< 
    std::function<std::future<int>(int)>, 
    std::function<int(int)> 
>; 

variant_t v1(std::move(f1)); // COMPILES (like it should) 
auto idx1 = v1.index(); // equals 0 

variant_t v2(std::move(f2)); // DOESN'T compile (like it should) 

नरक क्या है यहाँ जा रहा है? std::future<void> इतना खास क्यों है?

+1

नोट: 'std :: variant' एक सी ++ 17 सुविधा है, सी ++ 11 नहीं। – Rakete1111

+1

आपकी संकलन त्रुटि दूसरे उदाहरण से है, लेकिन आपका प्रश्न ऐसा लगता है कि यह आपके पहले से है। – Barry

उत्तर

16

variant का कनवर्ट करने वाला कन्स्ट्रक्टर टेम्पलेट अधिभार संकल्प को नियत करता है यह निर्धारित करने के लिए कि किस प्रकार की निर्मित वस्तु होनी चाहिए। विशेष रूप से, इसका मतलब है कि यदि उन प्रकारों के रूपांतरण समान रूप से अच्छे हैं, तो कन्स्ट्रक्टर काम नहीं करता है; आपके मामले में, यह iff काम करता है अगर std::function विशेषज्ञों में से एक विशेषज्ञ आपके तर्क से रचनात्मक है।

तो function<...> किसी दिए गए तर्क से कब रचनात्मक है? सी ++ 14 के रूप में, यदि तर्क पैरामीटर प्रकारों के साथ कॉल करने योग्य है और convertible to the return type एक प्रकार उत्पन्न करता है। ध्यान दें कि इस विनिर्देश के अनुसार, यदि वापसी का प्रकार void है, तो कुछ भी (any expression can be converted to void with static_cast) के रूप में जाता है। यदि आपके पास void लौटा रहा है, तो आपके द्वारा पास किया गया फ़ंक्शन — कुछ भी वापस कर सकता है, यह एक बग नहीं है! यही कारण है कि function<void(int)>f1 के लिए लागू है। दूसरी ओर, future<int>future<void> में परिवर्तित नहीं होता है; इसलिए केवल function<void(int)> व्यवहार्य है, और संस्करण के सूचकांक 1.

हालांकि दूसरे मामले में, लैम्ब्डा रिटर्न future<void>, जो future<void> और void दोनों के लिए परिवर्तनीय है,। जैसा ऊपर बताया गया है, यह function विशेषज्ञता दोनों व्यवहार्य होने का कारण बनता है, यही कारण है कि variant यह तय नहीं कर सकता कि कौन सा निर्माण करना है।

अंत में, यदि आप वापसी प्रकार को int पर समायोजित करते हैं, तो यह संपूर्ण void रूपांतरण समस्या से बचा जाता है, इसलिए सब कुछ अपेक्षित के रूप में कार्य करता है।

+0

आपके उत्तर के लिए धन्यवाद। लेकिन मैं अभी भी कल्पना नहीं कर सकता कि कैसे 'std :: भविष्य ' को 'void' (या' शून्य 'से 'std :: भविष्य ' में स्पष्ट रूप से परिवर्तनीय किया जा सकता है - मुझे यकीन नहीं है कि मैं इस बिंदु को समझ गया हूं)? क्या इसका मतलब यह है कि क्या कभी भी 'शून्य' में परिवर्तनीय है? –

+3

@DmitryKatkevich यह सही है, किसी भी अभिव्यक्ति को 'static_cast' के माध्यम से' शून्य 'में परिवर्तित किया जा सकता है। अन्यथा, हम 'फ़ंक्शन' में मज़दूरों की आपूर्ति नहीं कर सके जो 'फ़ंक्शन' उपज 'शून्य' होने पर कुछ लौटाते हैं, जो वांछित सुविधा है! – Columbo

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