2015-01-26 5 views
10

संकलित करने में विफल रहता है मैं C++ concurrency in action पढ़ रहा हूं। अध्याय 2.4 एक समानांतर_अंकुलेट एल्गोरिदम का वर्णन करता है।std :: रेफ तर्क के साथ धागा लेम्बा लेना

मैंने एक सीखने के प्रयोग के रूप में - एक सामान्य लैम्ब्डा के साथ उपयोग किए गए मज़ेदार को प्रतिस्थापित करने की कोशिश की।

#include <thread> 

template <typename T> 
struct f { 
    void operator() (T& result) { result = 1;} 
}; 

int main() { 
    int x = 0; 
    auto g = [](auto& result) { result = 1; }; 

    std::thread(f<int>(), std::ref(x)); // COMPILES 
    std::thread(g, std::ref(x));   // FAILS TO COMPILE 
} 

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

मैं नीचे करने के लिए संकलन त्रुटि आसुत किया है

In file included from /usr/include/c++/4.9/thread:39:0, 
       from foo.cpp:1: 
/usr/include/c++/4.9/functional: In instantiation of ‘struct std::_Bind_simple<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’: 
/usr/include/c++/4.9/thread:140:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = main()::<lambda(auto:1&)>&; _Args = {std::reference_wrapper<int>}]’ 
foo.cpp:13:31: required from here 
/usr/include/c++/4.9/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’ 
     typedef typename result_of<_Callable(_Args...)>::type result_type; 
                  ^
/usr/include/c++/4.9/functional:1695:9: error: no type named ‘type’ in ‘class std::result_of<main()::<lambda(auto:1&)>(std::reference_wrapper<int>)>’ 
     _M_invoke(_Index_tuple<_Indices...>) 
     ^

मेरे संकलक संस्करण

$ g++ --version 
g++ (Ubuntu 4.9.1-16ubuntu6) 4.9.1 

संकलन लैम्ब्डा के लिए क्यों असफल है लेकिन मज़ेदार नहीं?

संपादित करें: मैं जेनेरिक लैम्ब्डा के साथ क्या कर रहा हूं (रेफरी को असाइन करना) कैसे प्राप्त कर सकता हूं?

+2

'एफ' जेनेरिक लैम्ब्डा के बराबर नहीं है। टेम्पलेट 'ऑपरेटर()() 'होने से अधिक सटीक होगा (और यह वही त्रुटि उत्पन्न करेगा)। – 0x499602D2

+4

'std :: bind' को लक्ष्य फ़ंक्शन पर पास करने से पहले' context_wrapper' तर्कों को अनचाहे करने के लिए निर्दिष्ट किया गया है, इसलिए ['std :: thread (std :: bind (g, std :: ref (x)))' के रूप में कार्य करता है आप इरादा रखते हैं] (http://coliru.stacked-crooked.com/a/3f908c16c78c4027)। – Casey

उत्तर

17

टेम्पलेट तर्क कटौती रूपांतरणों को नहीं देखता है, उसी विषय पर एक और भिन्नता।

f<int> की operator()

void operator() (int& result); 

है जब आप इसे करने के लिए एक reference_wrapper<int> गुजरती हैं, रूपांतरण समारोह (operator int &) कहा जाता है, के लिए एक संदर्भ है कि result करने के लिए बाध्य किया जा सकता है उपज।

अपने सामान्य लैम्ब्डा की operator()

template<class T> void operator() (T& result) const; 

, यह एक reference_wrapper रूप T अनुमान है और फिर काम पर संकलित करने के लिए असफल है यदि यह एक reference_wrapper lvalue पारित किए गए। (एक reference_wrapper को असाइनमेंट "संदर्भ" reseats बजाय मूल्य को प्रभावित करता है।)

लेकिन यह भी है कि इससे पहले कि विफल रहता है, क्योंकि मानक की आवश्यकता है कि क्या आप std::thread को पारित prvalues ​​साथ प्रतिदेय होना चाहिए - और एक गैर स्थिरांक lvalue संदर्भ एक प्रसार से बंधे नहीं है। यह त्रुटि है जो आप देखते हैं - result_of में type नहीं है क्योंकि आपका फ़ैक्टर तर्क प्रकार के लिए कॉल करने योग्य नहीं है।आप g(std::ref(x)); करने के लिए प्रयास करते हैं, बजना produces एक नहीं बल्कि स्पष्ट त्रुटि:

main.cpp:16:5: error: no matching function for call to object of type '(lambda at main.cpp:11:14)' 
    g(std::ref(x)); 
    ^
main.cpp:11:14: note: candidate function [with $auto-0-0 = std::__1::reference_wrapper<int>] not viable: expects an l-value for 1st argument 
    auto g = [](auto& result) { result = 1; };   
    ^

आप शायद सिर्फ संदर्भ द्वारा प्रासंगिक स्थानीय कब्जा करने पर विचार करना चाहिए:

auto g = [&x]() { x = 1; }; 

या अगर किसी भी कारण से, आप एक सामान्य लैम्ब्डा का उपयोग करना चाहिए, तो आप मान द्वारा एक reference_wrapper ले सकता है (या स्थिरांक संदर्भ द्वारा), और फिर get() का उपयोग कर इसे खोलने:

auto g = [](auto result) { result.get() = 1; }; 

या शायद एक std::bind जोड़ने जो reference_wrapper रों खोलने जाएगा, जो देता है टेम्पलेट तर्क कटौती सही बात (टोपी टिप @Casey) कार्य करें:

std::thread(std::bind(g, std::ref(x))); 

या शायद इस reference_wrapper बकवास के साथ बांटना और लिखना अपने लैम्ब्डा एक गैर मालिक सूचक बजाय लेने के लिए:

auto g = [](auto* result) { *result = 1; }; 
std::thread(g, &x); 
+0

वाह! कभी-कभी झुकाव अद्भुत है। यही कारण है कि आपको एकाधिक कंपाइलर्स के साथ संदिग्ध कोड स्निपेट की जांच करनी चाहिए। – Drop

+1

@ ड्रोप वेल, निष्पक्ष होने के लिए, यह शानदार है जब आपने इसे सादा फ़ंक्शन कॉल 'g (std :: ref (x)) में घटा दिया; '; ओपी में मूल कोड पर यह वास्तव में कमाल नहीं है। –

+0

मैं तुमसे प्यार करता हूँ @ टी.सी. - मेरे जवाब को खोजने के लिए केवल 5 स्टैक ओवरफ्लो लिंक लिया। ('std :: thread (g, &x);') – Acidic

5

वहाँ "आह्वान (...)" कार्योंके परिवार के माध्यम से गुजर तर्क के साथ शामिल समस्याओं के सभी प्रकार के कर रहे हैं 210, std::bind, std::thread::thread। यदि आप ओवरलोडेड फ़ंक्शन नाम का उपयोग करना चाहते हैं, या एक लवल्यू संदर्भ पास करना चाहते हैं, या स्वर्ग संदर्भ द्वारा एक रावल्यू पास करने से मना कर देते हैं, तो आपको कठिन समय होगा। आप यहां SO के लिए आएंगे और हम में से एक जो प्रासंगिक अवशोषण सीखा है, वह आपको पास कर देगा। उम्मीद है कि अगली बार जब आप इसे आते हैं तो आपको याद होगा।

मुझे लगता है कि सी ++ 14 के बाद से सबसे अच्छा अभ्यास तर्कों से निपटने के लिए पूरी तरह से तर्कों को संभालने से बचने के लिए है और हमेशा इनवॉक्के को एक शून्य-तर्क फंक्शन प्रदान करता है जो वास्तविक लक्ष्य फ़ंक्शन द्वारा आवश्यक तर्कों को समाहित करता है। इसे स्वयं करने से आप वास्तव में उन अर्थशास्त्रों को प्राप्त करने की अनुमति देते हैं जो आप चाहते हैं कि प्रत्येक क्विर्क और वर्कअराउंड और इनवॉक परिवार के कार्यों के इंटरफेस में बढ़िया भेदों को जानने के बिना। सी ++ 14 सामान्यीकृत लैम्ब्डा कैप्चर किसी भी प्रकार के फ़ंक्शन और तर्कों के सेट को समाहित करने में काफी आसान बनाता है।

आपके मामले में, इस दृष्टिकोण परिणाम होगा में:

#include <thread> 

template <typename T> 
struct f { 
    void operator() (T& result) { result = 1;} 
}; 

int main() { 
    int x = 0; 
    auto g = [](auto& result) { result = 1; }; 

    std::thread([&]{ return f<int>{}(x); }); 
    std::thread([&]{ return g(x); }); 
} 

जो वास्तव में के रूप में इरादा करता है और अधिक पठनीय है।

std::reference_wrapper TR1 दिनों में बहुत अच्छा था जब हमें std::bind के माध्यम से संदर्भ पारित करने की आवश्यकता थी, लेकिन इसकी महिमा के दिन पिछले हैं और मुझे लगता है कि यह आधुनिक सी ++ में सबसे अच्छा बचा है।

+1

"अधिक पढ़ा जा सकता है" शायद तथ्य की तुलना में राय का एक और बयान है। यह देखते हुए कि मैं 'std :: bind' और' std :: context_wrapper' से बच रहा हूं सी ++ 11 के बाद प्लेग, लैम्बडास के लिए मेरी प्राथमिकता "पठनीय" के बारे में मेरा विचार पूर्वाग्रह करती है। – Casey

1

यहाँ अपनी समस्या को हल करने के लिए एक समारोह है। यह एक फ़ंक्शन ऑब्जेक्ट लेता है, और एक फ़ंक्शन ऑब्जेक्ट देता है जो आंतरिक फ़ंक्शन ऑब्जेक्ट को पास करने से पहले std::reference_wrapper s अनपैक करेगा।

#include <utility> 
#include <functional> 

template<class T> 
T&& unref(T&& t){return std::forward<T>(t);} 
template<class T> 
T& unref(std::reference_wrapper<T> r){ return r.get(); } 

template<class F> 
auto launder_refs(F&& f) { 
    return [f = std::forward<F>(f)](auto&&... args){ 
    return f(unref(std::forward<decltype(args)>(args))...); 
    }; 
} 

// 

    auto g = launder_refs([](auto& result) { result = 2; }); 

live example - अब g सिर्फ अपने मूल g तरह बर्ताव करता है, जब std::reference_wrapper है यह उन्हें result अंदर करने के लिए पारित करने से पहले संदर्भ के लिए उन्हें यह बदल जाता है पारित कर दिया छोड़कर।

आपकी समस्या एक std::reference_wrapper<T>&& अपने लैम्ब्डा के लिए पारित का कारण बनता है कि यह अनुमान करने के लिए एक U ऐसी है कि U& = std::reference_wrapper<T>&&, और कोई भी मौजूद कोशिश करने के लिए है।

संक्षेप में, यह टेम्पलेट फ़ंक्शंस में कटौती की एक सीमा है (यह रूपांतरणों पर विचार नहीं करता है), क्योंकि एक ही चरण में दोनों रूपांतरण और टेम्पलेट प्रकार की कटौती को मिश्रित करने से सभी को बल्लेबाजी होगी।

उपर्युक्त कोड अंतर्निहित लैम्ब्डा क्लोजर (या फ़ंक्शन ऑब्जेक्ट) से std::reference_wrapper एस छुपाता है। यह कम से कम ओवरहेड के साथ भी ऐसा करता है।

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