2017-05-04 9 views
13

std::bind और std::thread कुछ डिज़ाइन सिद्धांत साझा करें। के बाद से उन दोनों को पारित कर दिया तर्क करने के लिए इसी स्थानीय objets की दुकान, हम अगर संदर्भ अर्थ विज्ञान वांछित है या तो std::ref या std::cref उपयोग करने की आवश्यकता:बाय-रेफ तर्क: क्या यह std :: thread और std :: bind के बीच एक असंगतता है?

void f(int& i, double d) { /*...*/ } 

void g() { 
    int x = 10; 
    std::bind(f, std::ref(x), _1) (3.14); 
    std::thread t1(f, std::ref(x), 3.14); 
    //... 
} 

लेकिन मैं हाल ही में एक निजी खोज द्वारा intrigued रहा हूँ: std::bind करने के लिए आप की अनुमति देगा उपरोक्त मामले में एक मूल्य पास करें, भले ही यह आमतौर पर कोई नहीं चाहता है।

std::bind(f, x, _1) (3.14); // Usually wrong, but valid. 

हालांकि, यह std::thread के लिए सच नहीं है। निम्नलिखित एक संकलन त्रुटि ट्रिगर करेगा।

std::thread t2(f, x, 3.14); // Usually wrong and invalid: Error! 

पहली बार मैंने सोचा कि यह एक कंपाइलर बग था, लेकिन त्रुटि वास्तव में वैध है। ऐसा लगता है कि std::thread के कन्स्ट्रक्टर का टेम्पलेट संस्करण copy decayingआवश्यकता (int&int में) 30.3.1.2 द्वारा लगाए गए तर्कों को सही ढंग से कम करने में सक्षम नहीं है।

सवाल यह है: std::bind के तर्कों के समान कुछ क्यों नहीं चाहिए? या यह स्पष्ट असंगतता का इरादा है?

नोट: समझाया गया है कि यह नीचे दी गई टिप्पणी में डुप्लीकेट क्यों नहीं है।

+2

यह डुप्लिकेट नहीं है। * वह * प्रश्न बता रहा है कि * मेरे * प्रश्न का प्रीमियर क्या है। इसके अलावा, यह std :: धागा को स्पर्श नहीं करता है। असल में, जो मैं इंगित कर रहा हूं वह वहां टिप्पणियों में से एक का उदाहरण है (लगभग std :: धागे के निर्माता। –

उत्तर

8

bind द्वारा लौटाया गया फ़ंक्शन ऑब्जेक्ट पुन: उपयोग के लिए डिज़ाइन किया गया है (यानी, कॉल को कई बार कहा जाता है); इसलिए इसे अपने बाध्य तर्कों को अंतराल के रूप में पारित करना होगा, क्योंकि आप उन तर्कों से आगे बढ़ना नहीं चाहते हैं या बाद में कॉल एक बाएं तर्क से चलने वाले तर्क को देखेंगे। (इसी प्रकार, आप फ़ंक्शन ऑब्जेक्ट को एक लवल्यू के रूप में भी जाना चाहते हैं।)

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

+2

^^ यह। और पहले पैराग्राफ के कारणों के कारण, आप 'std :: bind' के परिणाम को एक राज्यव्यापी फ़ंक्शन ऑब्जेक्ट के रूप में उपयोग कर सकते हैं, क्योंकि इसमें गैर स्थैतिक डेटा सदस्य हैं जिन्हें लक्षित ऑब्जेक्ट में संशोधित किया जा सकता है और संशोधित किया जा सकता है, और कॉल के बीच राज्य बनाए रखें। एम उपयोग योग्य lambdas प्रतिलिपि द्वारा कब्जा चर के लिए एक समान संपत्ति है। यह डिजाइन द्वारा है और 'std :: bind' में कोई दोष नहीं है। –

6

std::bind लैम्बडास के अस्तित्व के कारण पहुंचने पर ज्यादातर अप्रचलित था। सी ++ 14 सुधार और सी ++ 17 std::apply के साथ, bind के लिए शेष उपयोग के मामले काफी ज्यादा चले गए हैं।

सी ++ 11 में भी ऐसे मामले जहां bind एक समस्या हल हो गई है जहां एक लैम्ब्डा हल नहीं करता है जहां अपेक्षाकृत दुर्लभ है।

दूसरी ओर, std::thread थोड़ा अलग समस्या हल कर रहा था। इसे "हर मुद्दे को हल करने" के लिए bind की लचीलापन की आवश्यकता नहीं है, इसके बजाय यह आमतौर पर खराब कोड को अवरुद्ध कर सकता है।

bind मामले में f पर संदर्भित संदर्भ x नहीं होगा बल्कि x की आंतरिक संग्रहीत प्रति के संदर्भ में होगा। यह बेहद आश्चर्यजनक है।

void f(int& x) { 
    ++x; 
    std::cout << x << '\n'; 
}; 

int main() { 
    int x = 0; 
    auto b = std::bind(f, x); 
    b(); 
    b(); 
    b(); 
    std::cout << x << '\n'; 
} 

प्रिंट

1 
2 
3 
0 

जहां पिछले 0 मूल x है, f में संग्रहीत x की वृद्धि की जाती प्रतिलिपि 12 और 3 रहे हैं, जबकि।

लैम्ब्डा के साथ, एक परिवर्तनीय संग्रहीत स्थिति और बाहरी संदर्भ के बीच का अंतर स्पष्ट किया जा सकता है।

auto b = [&x]{ f(x); }; 

बनाम

auto b = [x]()mutable{ f(x); }; 

जिनमें से एक प्रतियां x फिर उस पर बार-बार f invokes, अन्य f में x के लिए एक संदर्भ से गुजरता है।

के साथ f को बिना किसी संदर्भ के x की संग्रहीत प्रति तक पहुंचने के लिए ऐसा करने का कोई तरीका नहीं है।

std::thread के लिए, यदि आप इस परिवर्तनीय स्थानीय प्रतिलिपि व्यवहार चाहते हैं तो आप केवल लैम्ब्डा का उपयोग करें।

std::thread t1([x]()mutable{ f(x); }); 

वास्तव में, मैं सी ++ 11 में आह्वान वाक्य रचना के सबसे तर्क है नहीं सी ++ 14 शक्ति lambdas और भाषा में std::apply की विरासत हो रहा है। लैम्ब्डा और std::apply द्वारा हल किए जाने वाले बहुत कम मामले हैं (लागू होने की आवश्यकता है क्योंकि लैम्ब्डा आसानी से चलने वाले पैक का समर्थन नहीं करते हैं और फिर उन्हें अंदर छोड़ते हैं)।

लेकिन हमारे पास टाइम मशीन नहीं है, इसलिए हमारे पास सी ++ में किसी विशिष्ट संदर्भ में कुछ का आह्वान करने के विचार को व्यक्त करने के लिए कई समानांतर तरीके हैं।

+1

std :: बाइंड अप्रचलित नहीं है (इसलिए आज तक) इसे सी ++ 11 में पेश किया गया है lambdas के साथ। वास्तव में lambdas के परिचय के साथ अप्रचलित हो गया था std :: mem_fn या std :: bind1st (std :: bind2nd) जैसी चीजें थीं। वास्तव में, std :: bind * c *+ में आंशिक फ़ंक्शन एप्लिकेशन का केवल * तंत्र है (https://en.wikipedia.org/wiki/Partial_application) –

+1

@LeandroT।सी। मेलो '[x] (ऑटो और ... ... तर्क) -> decltype (ऑटो) {वापसी एफ (एक्स, decltype (तर्क) (तर्क) ...); } 'मैंने अभी' आंशिक रूप से 'x' को 'b'' के उपयोग के साथ आंशिक रूप से लागू किया है। अधिक जटिल मामलों में, अधिक जटिल मामलों का उपयोग किया जा सकता है। तर्कों का एक सेट बाध्यकारी की तरह। – Yakk

+1

दिलचस्प समाधान। मुझे नहीं लगता कि सदस्य कार्य के साथ काम (बॉक्स के बाहर) काम करेगा ...? वैसे भी, मुझे केवल * लाइब्रेरी घटक * कहा जाना चाहिए था। एक मायने में, अगर हम * डी-शर्किंग * स्टैंडपॉइंट से सोचते हैं, तो हम "अप्रचलित" चीज़ों का एक समूह मान सकते हैं। –

6

मैं क्या बता सकते हैं, thread मूल रूप से bind रूप में एक ही नियम के साथ शुरुआत की, लेकिन N3090 द्वारा 2010 में संशोधित किया गया था बाधा आप की पहचान की है पर लेने के लिए।

विभिन्न योगदानों को विभाजित करने के लिए इसका उपयोग करके, मेरा मानना ​​है कि आप LWG issue 929 की तलाश में हैं।

विडंबना यह है कि यह इरादा thread कन्स्ट्रक्टर कम को बाधित करने के लिए प्रतीत होता है। निश्चित रूप से bind का कोई उल्लेख नहीं है, हालांकि this wording was later also applied to async (एलडब्ल्यूजी 1315 के बाद "क्लीन-अप" अनुभाग), तो मैं कहूंगा कि bind पीछे छोड़ दिया गया है।

यह सुनिश्चित करना काफी मुश्किल है, इसलिए, मैं the committee itself पूछने की सलाह दूंगा।

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