2011-09-23 19 views
13

यह शायद एक दार्शनिक सवाल है, लेकिन मैं निम्नलिखित समस्या में पड़ गए:std :: फ़ंक्शन उदाहरणों का डिफ़ॉल्ट कन्स्ट्रक्टर क्यों होता है?

आप एक std :: समारोह को परिभाषित है, और आप इसे सही ढंग से शुरू नहीं किया जा रहा है, तो आपके आवेदन इस तरह, दुर्घटना होगा:

typedef std::function<void(void)> MyFunctionType; 
MyFunctionType myFunction; 
myFunction(); 

समारोह एक तर्क के रूप पारित हो जाता है, तो इस तरह:

void DoSomething (MyFunctionType myFunction) 
    { 
    myFunction(); 
    } 

तो, ज़ाहिर है, यह भी दुर्घटनाओं। इसका मतलब है कि मैं इस तरह की जाँच के कोड जोड़ने के लिए मजबूर कर रहा हूँ:

void DoSomething (MyFunctionType myFunction) 
    { 
    if (!myFunction) return; 
    myFunction(); 
    } 

इन चेकों की आवश्यकता होती है मुझे पुराने सी दिनों, जहां आप भी स्पष्ट रूप से सभी सूचक तर्क की जांच करने के लिए किया था करने के लिए एक फ्लैश-बैक देता है:

void DoSomething (Car *car, Person *person) 
    { 
    if (!car) return;  // In real applications, this would be an assert of course 
    if (!person) return; // In real applications, this would be an assert of course 
    ... 
    } 

void DoSomething (Car &car, Person &person) 
    { 
    // I can assume that car and person are valid 
    } 

तो,:

सौभाग्य से, हम सी ++, जो मुझे इन चेक (लेखन यह सोचते हैं कि फोन करने वाले कार्य करने के लिए एक nullptr की सामग्री में सफल नहीं हुए से रोकता में संदर्भ का उपयोग कर सकते हैं y do std :: फ़ंक्शन इंस्टेंस में एक डिफ़ॉल्ट कन्स्ट्रक्टर है? डिफ़ॉल्ट कन्स्ट्रक्टर के बिना आपको किसी अन्य फ़ंक्शन के सामान्य तर्कों की तरह चेक जोड़ने की ज़रूरत नहीं है। और उन 'दुर्लभ' मामलों में जहां आप 'वैकल्पिक' std :: फ़ंक्शन को पास करना चाहते हैं, आप अभी भी एक पॉइंटर पास कर सकते हैं (या बूस्ट :: वैकल्पिक का उपयोग करें)।

+4

यह क्रैश नहीं होता है; यह एक अपवाद फेंकता है। –

+0

'functors' के बारे में पढ़ें: http://www.sgi.com/tech/stl/functors.html –

+0

" मुझे चेक कोड जोड़ने के लिए मजबूर होना पड़ता है "- शर्म की बात है कि आपके कॉलर इसके बजाय अपना कोड ठीक नहीं कर सकते हैं। अपवाद को संभालने के लिए उन्हें बचाने के लिए निरस्त करने के लिए अजीब लगता है। फिर भी, यह खराब हो सकता है, अगर आपने कोई फ़ंक्शन पॉइंटर लिया और उन्होंने इसे प्रारंभ करने से परेशान नहीं किया, तो इसका अनिश्चित मूल्य होगा और व्यवहार अपरिभाषित होगा। वे शायद आपके काम को बुलाए जाने की तुलना में 'स्ट्रेल' को बुरी तरह बुरी तरह गड़बड़ कर रहे हैं। –

उत्तर

14

यह सच है, लेकिन यह अन्य प्रकार के लिए भी सच है। जैसे अगर मैं चाहता हूं कि मेरी कक्षा में एक वैकल्पिक व्यक्ति हो, तो मैं अपना डेटा सदस्य एक व्यक्ति-सूचक बना देता हूं। Std :: फ़ंक्शंस के लिए ऐसा क्यों न करें? Std :: फ़ंक्शन के बारे में इतना खास क्या है कि इसमें 'अमान्य' स्थिति हो सकती है?

इसमें "अवैध" स्थिति नहीं है। यह कोई इस से अमान्य है:

std::vector<int> aVector; 
aVector[0] = 5; 

क्या आपके पास एक खालीfunction है, aVector जैसे एक खाली vector है। वस्तु एक बहुत अच्छी तरह से परिभाषित राज्य में है: डेटा नहीं होने की स्थिति।

void CallbackRegistrar(..., std::function<void()> *pFunc); 

कैसे आपको लगता है कि कॉल करने के लिए है:

अब, सुझाव के "कार्य करने के लिए सूचक" अपने पर विचार करते हैं? ठीक है, यहाँ एक बात आप ऐसा नहीं कर सकते हैं:

void CallbackFunc(); 
CallbackRegistrar(..., CallbackFunc); 

जिसकी अनुमति नहीं है क्योंकि CallbackFunc एक समारोह है, जबकि पैरामीटर प्रकार एक std::function<void()>* है। वे दो परिवर्तनीय नहीं हैं, इसलिए संकलक शिकायत करेगा। तो आदेश कॉल करने के लिए, यदि आप ऐसा करने के लिए है:

void CallbackFunc(); 
CallbackRegistrar(..., new std::function<void()>(CallbackFunc)); 

तुम बस चित्र में new शुरू की है। आपने संसाधन आवंटित किया है; इसके लिए जिम्मेदार कौन होगा? CallbackRegistrar? जाहिर है, आप स्मार्ट सूचक किसी तरह का उपयोग करने के लिए चाहते हो सकता है, तो आप इंटरफ़ेस को अस्त-व्यस्त साथ और भी अधिक:

void CallbackRegistrar(..., std::shared_ptr<std::function<void()>> pFunc); 

एपीआई झुंझलाहट और cruft के लिए बहुत कुछ है यही कारण है कि, बस के चारों ओर एक समारोह पारित करने के लिए। इससे बचने का सबसे आसान तरीका std::functionखाली होने की अनुमति देना है। जैसे हम std::vector खाली होने की अनुमति देते हैं। जैसे हम खाली होने के लिए std::string की अनुमति देते हैं। जैसे हम std::shared_ptr खाली होने की अनुमति देते हैं। और इसी तरह।

इसे आसानी से रखने के लिए: std::functionमें एक फ़ंक्शन शामिल है। यह एक कॉल करने योग्य प्रकार के लिए एक धारक है। इसलिए, संभावना है कि इसमें कोई कॉल करने योग्य प्रकार न हो।

+2

जाहिर है, आप 'boost :: वैकल्पिक MSalters

+3

@MSalters: हाँ, यह काम करेगा। लेकिन दुख की बात है कि 'वैकल्पिक' सी ++ मानक पुस्तकालय का हिस्सा नहीं है। और उन गरीब, दुर्भाग्यपूर्ण आत्माओं के लिए जिन्हें बूस्ट का उपयोग करने की अनुमति नहीं है, यह बस एक विकल्प नहीं है। –

+0

वोट दें, अच्छा शब्द: वैकल्पिक ... यह बस एक विकल्प नहीं है, हाहा! –

5

std::function के लिए सबसे आम उपयोग मामलों में से एक कॉलबैक पंजीकृत करना है, कुछ शर्तों को पूरा होने पर बुलाया जाना है। अनियमित उदाहरणों के लिए अनुमति देने पर केवल कॉलबैक पंजीकृत करना संभव हो जाता है, अन्यथा आपको कम से कम कुछ प्रकार के नो-ऑप फ़ंक्शन को पास करने के लिए मजबूर होना पड़ता है।

+0

सच है, लेकिन यह अन्य प्रकारों के लिए भी सच है। जैसे अगर मैं चाहता हूं कि मेरी कक्षा में एक वैकल्पिक व्यक्ति हो, तो मैं अपना डेटा सदस्य एक व्यक्ति-सूचक बना देता हूं। Std :: फ़ंक्शंस के लिए ऐसा क्यों न करें? Std :: फ़ंक्शन के बारे में इतना खास क्या है कि इसमें 'अमान्य' स्थिति हो सकती है? – Patrick

+4

मेरी राय में आपको std :: फ़ंक्शन को कॉलबेल के लिए एक स्मार्ट पॉइंटर के रूप में देखना चाहिए। आप एक share_ptr को पॉइंटर का उपयोग करने की उम्मीद नहीं करेंगे, है ना? –

1

ऐसे मामले हैं जहां आप निर्माण पर सबकुछ शुरू नहीं कर सकते हैं (उदाहरण के लिए, जब एक पैरामीटर दूसरे निर्माण पर प्रभाव पर निर्भर करता है जो बदले में पहले प्रभाव पर निर्भर करता है ...)।

इन मामलों में, आपको लूप को तोड़ना होगा, बाद में एक पहचान योग्य अमान्य स्थिति को सही करने के लिए स्वीकार करना होगा। तो आप पहले को "शून्य" के रूप में बनाते हैं, दूसरा तत्व बनाते हैं, और पहले को पुनः निर्दिष्ट करते हैं।

आप वास्तव में चेक से बच सकते हैं, अगर कहीं भी फ़ंक्शन का उपयोग किया जाता है- आप इसे उस ऑब्जेक्ट के निर्माता के अंदर देते हैं जो इसे एम्बेड करता है, तो आप हमेशा वैध पुन: असाइनमेंट के बाद वापस आ जाएंगे।

+0

मुझे नहीं लगता कि रिक्त std :: फ़ंक्शन "बाद में सही" करने का कोई तरीका है। यदि यह सृजन में खाली है, तो यह हमेशा खाली रहता है। (बेशक, चाहे आप इसे एक अमान्य स्थिति मानते हों या नहीं, एक अलग मामला है।) – max

+0

@ मैक्स मेरे लिए "एक ही अवधारणा - अलग-अलग शब्द" समस्या जैसा दिखता है। –

+0

मुझे लगता है कि मैं तब आपके तर्क को समझ नहीं पाया। आप कह रहे हैं कि आप खाली 'std :: function' का उपयोग कर सकते हैं जब आप नहीं जानते कि बाद में आप किस मूल्य के लिए चाहते हैं (यानी, आप इसे ctor में प्रारंभ नहीं कर सकते हैं)। मैं मानता हूं कि एक अच्छा उपयोग मामला होगा। जो मैं नहीं देखता, वह है जो आप बाद में करेंगे - जब आप आखिरकार * उस मूल्य को जानते हैं जो आप चाहते हैं। ऐसा नहीं है कि आप किसी खाली 'std :: function' को किसी और चीज़ में बदल सकते हैं? – max

5

उत्तर शायद ऐतिहासिक है: std::function फ़ंक्शन पॉइंटर्स के प्रतिस्थापन के रूप में है, और फ़ंक्शन पॉइंटर्स की क्षमता NULL होने की क्षमता थी। इसलिए, जब आप फ़ंक्शन पॉइंटर्स को आसान संगतता प्रदान करना चाहते हैं, तो आपको एक अमान्य स्थिति प्रदान करने की आवश्यकता है।

जैसा कि आपने बताया है, पहचानने योग्य अमान्य स्थिति वास्तव में आवश्यक नहीं है, boost::optional यह काम ठीक है। तो मैं कहूंगा कि std::function इतिहास के लिए बस वहां हैं।

9

वास्तव में, आपका आवेदन क्रैश नहीं होना चाहिए।

§ 20.8.11.1 कक्षा bad_function_call [func.wrap।badcall]

1/ प्रकार bad_function_call की एक अपवाद function::operator() (20.8.11.2.4) समारोह आवरण वस्तु कोई लक्ष्य नहीं है जब से फेंक दिया है।

व्यवहार पूरी तरह से निर्दिष्ट है।

+2

यह निर्दिष्ट किया जा सकता है, लेकिन विनिर्देश यह भी कहता है कि कार्यक्रम 'मुख्य' छोड़ने के अपवाद की स्थिति में समाप्त होता है (यानी क्रैश)। यदि आप इसे पकड़ नहीं पाते तो क्या होता है। –

+2

@ निकोलबोलस: आप सही हैं, लेकिन मैं वास्तव में यह एक दुर्घटना नहीं मानता क्योंकि कई परिचालन संभावित रूप से अपवाद उठा सकते हैं: 'std :: function' को कुछ निर्दिष्ट करने का सरल तथ्य स्मृति आवंटन कर सकता है। इसलिए, अपवादों को संभालने से इस तथ्य से पूरी तरह से असंबंधित नहीं है कि 'std :: function' को कॉलबैक के साथ प्रारंभ नहीं किया जा सकता है। –

0

उसी तरह से आप एक मज़ेदार प्रकार में एक नलस्टेट जोड़ सकते हैं जिसमें एक नहीं है, आप एक मल्टीक्टर को उस कक्षा के साथ लपेट सकते हैं जो एक नलस्टेट स्वीकार नहीं करता है। पूर्व को राज्य जोड़ने की आवश्यकता है, बाद में नए राज्य की आवश्यकता नहीं है (केवल एक प्रतिबंध)। इस प्रकार, जबकि मुझे std::function डिज़ाइन के तर्क को नहीं पता है, यह सबसे अधिक दुबला & औसत उपयोग का समर्थन करता है, इससे कोई फर्क नहीं पड़ता कि आप क्या चाहते हैं।

चीयर्स & hth।,

0

तुम बस एसटीडी का उपयोग :: कॉलबैक के लिए समारोह, आप एक सरल टेम्पलेट सहायक समारोह है कि हैंडलर को अपने तर्कों अग्रेषित करता है का उपयोग कर सकते हैं अगर यह खाली नहीं है:

template <typename Callback, typename... Ts> 
void SendNotification(const Callback & callback, Ts&&... vs) 
{ 
    if (callback) 
    { 
     callback(std::forward<Ts>(vs)...); 
    } 
} 

और निम्नलिखित तरीके से इसका इस्तेमाल करते हैं:

std::function<void(int, double>> myHandler; 
... 
SendNotification(myHandler, 42, 3.15); 
संबंधित मुद्दे