2012-06-02 20 views
5

मान लीजिए मैं एक समारोह है कि कुछ साइड इफेक्ट करता है और फिर एक जवाब देता है:समारोह संकेत और वापसी प्रकार रूपांतरण

int foo() 
{ 
    perform_some_side_effect(); 
    return 42; 
} 

मैं एक समारोह सूचक को foo बाध्य करने के लिए चाहते हैं, लेकिन मैं में कोई दिलचस्पी नहीं हूँ जवाब है, बस पक्ष प्रभाव:

error: invalid conversion from ‘int (*)()’ to ‘void (*)()’ 
:

void (*bar)() = foo; 

बहरहाल, यह एक प्रकार की त्रुटि हो गया लगता है

उस त्रुटि के पीछे तर्क क्या है? टाइप सिस्टम मुझे जवाब को अनदेखा क्यों नहीं करता है?


एक तरफ ध्यान दें पर, यह काम करता है अगर मैं में समारोह सूचक लपेट एक std::function:

std::function<void()> baz = foo; 

कैसे std::function (जाहिरा तौर पर) प्रकार प्रणाली में इस प्रतिबंध को नाकाम करने के लिए प्रबंधन करता है?

+1

तर्कसंगत सिर्फ इतना है कि वे विभिन्न प्रकार हैं है बनाने के लिए कुछ प्रवंचना लेता है। आप 'reinterpret_cast' के साथ इसे (अपने जोखिम पर) ओवरराइड कर सकते हैं। –

+0

@ स्टेवनबर्नैप: सवाल यह है कि क्यों * वे परिवर्तित नहीं कर सकते हैं। यह स्पष्ट है कि वे विभिन्न प्रकार हैं। एक 'फ्लोट' या तो 'int' नहीं है, लेकिन आप उन्हें परिवर्तित कर सकते हैं। – Puppy

+0

मुझे लगता है कि ऐसा इसलिए है क्योंकि एक सामान्य नियम के रूप में निहित पॉइंटर्स को अनुमति नहीं है। –

उत्तर

9

What is the rationale behind that error? Why doesn't the type system allow me to ignore the answer?

कारण यह है कि प्रकार अलग हैं, और कॉल की जगह (समारोह सूचक के माध्यम से) में उत्पन्न कोड अलग है। एक कॉलिंग सम्मेलन पर विचार करें जहां वापसी के मूल्य के लिए सभी तर्क स्टेक और स्पेस पर लिखे गए हैं, यह भी ढेर में आरक्षित है। यदि कॉल void (*)() के माध्यम से जाता है तो वापसी मूल्य के लिए स्टैक में कोई स्थान आरक्षित नहीं किया जाएगा, लेकिन फ़ंक्शन (इसे कैसे कहा जा रहा है से अनजान) अभी भी उस स्थान पर 42 लिख देगा जहां कॉलर को आरक्षित स्थान होना चाहिए।

How does std::function (apparently) manage to circumvent this restriction in the type system?

ऐसा नहीं है। यह एक फ़ंक्शन ऑब्जेक्ट बनाता है जो कॉल को वास्तविक फ़ंक्शन पर लपेटता है। अब जब संकलक foo करने के लिए कॉल संसाधित करता

void operator()() const { 
    foo(); 
} 

यह जानता है कि यह तो बुला परंपरा के अनुसार कॉल एक समारोह है कि एक int और यह करना होगा रिटर्न के लिए क्या करना है क्या: यह की तरह एक सदस्य शामिल होंगे । चूंकि टेम्पलेट वापस नहीं आता है, यह केवल मूल्य को अनदेखा कर देगा - वास्तव में वापस लौटाया गया था।

+1

+1: सबसे पूरा उत्तर। –

1

std::function केवल स्रोत संगत होने की आवश्यकता है - यानी, यह एक नई कक्षा उत्पन्न कर सकता है जो परिणाम को अनदेखा करता है जो नए कैलिंग कोड उत्पन्न करता है। फ़ंक्शन पॉइंटर बाइनरी संगत होना चाहिए और वह नौकरी नहीं कर सकता- void(*)() और int(*)() सटीक कोड पर इंगित करें।

1

आप std::function<> निवारण हेतु ऐसा करने के बारे में सोच सकते हैं:

void __func_void() 
{ 
    foo(); 
} 

यह वास्तव में है कि तुलना में थोड़ा और अधिक जटिल है, लेकिन मुद्दा यह है कि यह नहीं करने के लिए प्रकार विलोपन के साथ एक साथ टेम्पलेट कोड उत्पन्न करता है विशिष्टताओं के बारे में परवाह है।

1

दूसरों के कहने के अलावा, कॉलर को यह जानने के लिए रिटर्न प्रकार की भी आवश्यकता है कि परिणामस्वरूप इसे किस विनाशक को बुलाया जाना चाहिए (वापसी मूल्य अस्थायी हो सकता है)।


दुर्भाग्य यह है नहीं के रूप में आसान

auto (*bar)() = foo; 

जीसीसी और बजना यह स्वीकार करते हैं यद्यपि के रूप में। मुझे यह देखने के लिए spec को फिर से जांचना होगा कि यह वास्तव में सही है या नहीं।

अद्यतन: कल्पना का कहना है

The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.

यह जब तेजी से पढ़ा है, लेकिन इस जीसीसी और बजना द्वारा करने के लिए लागू किया गया है गुमराह किया जा सकता केवल उच्चस्तरीय declarator पर लागू होते हैं। हमारे मामले में, यह एक सूचक घोषणाकर्ता है। इसमें निहित घोषणाकर्ता एक समारोह घोषणाकर्ता है। तो void के लिए बस auto को प्रतिस्थापित करें और फिर संकलक आपके लिए प्रकार कम कर देगा।


वैसे, आप मैन्युअल रूप से कभी यह काम कर सकते हैं, लेकिन यह यह काम

template<typename FunctionType> 
struct Params; 

template<typename ...Params> 
struct Params<void(Params...)> { 
    template<typename T> 
    using Identity = T; 

    template<typename R> 
    static Identity<R(Params...)> *get(R f(Params...)) { 
    return f; 
    } 
}; 

// now it's easy 
auto bar = Params<void()>::get(foo); 
+0

यहां तक ​​कि यह वीएस2011 आरसी में संकलित है। ऑटो बार = टेस्ट; \t बार(); – Jagannath

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