2012-06-26 19 views
6

पर विशेषज्ञता टाइप करें मैं एक कक्षा लिख ​​रहा हूं जो std::function (या कम से कम कक्षाएं कई तरीकों से समान हैं) के साथ कई अलग-अलग विशेषताएं साझा करती हैं। जैसा कि आप सभी जानते हैं std::function टेम्पलेट पैरामीटर (यानी std::function<void (std::string&)>) निर्दिष्ट करके तत्काल है, यह मेरी कक्षा के लिए समान है। हालांकि, मेरे पास अपवाद है, यदि मैं वापसी मान शून्य है (std::function<"return value" ("parameters">), तो मैं अपनी कक्षा में एकल फ़ंक्शन का विशेषज्ञ बनाना चाहता हूं। मुझे संकलन समय पर ऐसा करने की ज़रूरत है, और मैं इसे काम नहीं कर सकता क्योंकि इसे करना चाहिए। यहाँ विवरण के लिए कुछ परीक्षण कोड है:संकलन-समय

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 
     if(std::is_void<Ret>::value) 
     { 
      // Do something... 
     } 

     else /* Not a void function */ 
     { 
      Ret returnVal; 
      return returnVal; 
     } 
    } 
}; 

int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
} 

आप स्पष्ट रूप से देख सकते हैं, अगर संकलक ऊपर की परीक्षा में 'शेष' शाखा को दूर नहीं करता, मेरे कोड एक शून्य मूल्य बनाने की कोशिश करेंगे (यानी void returnVal;) । समस्या यह है कि संकलक तो मैं एक संकलक त्रुटि के साथ अंत शाखा को दूर नहीं करता है:

./test.cpp: In instantiation of ‘Ret Test::operator()(Args ...) [with Ret = void; Args = {char}]’: ./test.cpp:27:10: required from here ./test.cpp:18:8: error: variable or field ‘returnVal’ declared void ./test.cpp:19:11: error: return-statement with a value, in function returning 'void' [-fpermissive]

एक सामान्य रूप से उपयोग std::enable_ifstd::is_void के साथ संयुक्त होता है, समस्या यह है कि मैं पर विशेषज्ञ नहीं करना चाहती है फ़ंक्शन टेम्पलेट, लेकिन कक्षा टेम्पलेट पर।

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    typename std::enable_if<!std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     Ret returnVal; 
     return returnVal; 
    } 

    typename std::enable_if<std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     // It's a void function 
     // ... 
    } 
}; 

अगर मैं बजाय ऊपर कोड का उपयोग मैं भी अधिक त्रुटियों के साथ और एक समाधान

./test.cpp:11:2: error: expected ‘;’ at end of member declaration 
./test.cpp:11:2: error: declaration of ‘typename std::enable_if<(! std::is_void<_Tp>::value), Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:11:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:2: error: expected ‘;’ at end of member declaration 
./test.cpp:18:2: error: declaration of ‘typename std::enable_if<std::is_void<_Tp>::value, Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:18:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:6: error: ‘int Test<Ret(Args ...)>::operator()(Args ...)’ cannot be overloaded 
./test.cpp:11:6: error: with ‘int Test<Ret(Args ...)>::operator()(Args ...)’ 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...)’: 
./test.cpp:22:2: warning: no return statement in function returning non-void [-Wreturn-type] 
./test.cpp: In instantiation of ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:28:10: required from here 
./test.cpp:13:7: error: variable or field ‘returnVal’ declared void 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:15:2: warning: control reaches end of non-void function [-Wreturn-type] 

अगर मैं सिर्फ सादा गूंगा कर रहा हूँ मैं माफी चाहता हूँ बिना खत्म, और जवाब स्पष्ट है। मैं टेम्पलेट्स के लिए बिल्कुल नया हूं और मुझे किसी भी अन्य थ्रेड/प्रश्नों में एक उपयुक्त उत्तर नहीं मिला।

+0

"स्थिर अगर" शामिल करने का एक प्रस्ताव है जो आपके पहले उदाहरण में वही करेगा जो आप चाहते हैं। – mfontanini

+0

@mfontanini: क्या वास्तव में आगे बढ़ने वाले दो प्रस्तावों में से कोई एक अलग मुद्दा है। उनके पास कमियां होती हैं और कुछ चीजें उन चीज़ों से अधिक जटिल हो जाती हैं, जिन्हें आप टेम्पलेट्स के साथ SFINAE का उपयोग करना शुरू करते हैं, जिसमें स्थिर हैं- –

+0

"समस्या यह है कि संकलक शाखा को नहीं हटाता है इसलिए मैं एक कंपाइलर त्रुटि के साथ समाप्त होता हूं" = > दुर्भाग्यवश, कंपाइलर * की आवश्यकता नहीं है *, जो (मैं स्वीकार करता हूं) थोड़ा अचूक है। –

उत्तर

7

ऐसी कुछ चीजें हैं जो आपके विवरण से बिल्कुल स्पष्ट नहीं हैं, इसलिए मैं सबसे सामान्य उत्तर से शुरू करूंगा।

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

template <typename T, typename... Args> 
struct tmpl_base { 
    T operator()(Args... args) { 
     //generic 
    } 
}; 
template <typename... Args> 
struct tmpl_base<void,Args...> { 
    void operator()(Args... args) { 
    } 
}; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> : tmp_base<Ret,Args...> { 
    // do not declare/define operator(), maybe bring the definition into scope: 
    using tmp_base<Ret,Args...>::operator(); 

    // Rest of the class 

इस अपने खाके में केवल समारोह है, तो आंशिक विशेषज्ञता एक बहुत सरल समाधान विरासत कोस की आवश्यकता नहीं है कि है।

+0

मैं सिद्धांत रूप से, रचनात्मक कार्यों (या यहां, एक मुक्त फ़ंक्शन में प्रतिनिधिमंडल) के दौरान विरासत का उपयोग करने की सलाह देता हूं, लेकिन मुझे यह मानना ​​है कि मैं शायद कुछ भी नहीं करूँगा। –

+0

@MatthieuM।: शायद यह विरासत का एकमात्र दुर्व्यवहार है, मुझे विश्वास है कि एक आवश्यक बुराई है। मुझे नहीं लगता कि इस मामले में आप यहां प्रतिनिधिमंडल का कोई भी छोटा हिस्सा कर सकते हैं, क्योंकि मुख्य मुद्दा यह है कि अभिव्यक्ति 'रिटर्न प्रतिनिधि (तर्क ...)' उन मामलों के लिए खराब हो जाएगी जहां 'शून्य' वापस आती है । यह विशेष मुद्दा किसी रचनाकृत ऑब्जेक्ट या किसी भिन्न फ़ंक्शन को प्रतिनिधि को अवरुद्ध करता है। –

+0

हम अनुमान लगाएंगे कि इसे करना होगा। यह दुखद है कि इस तरह के एक साधारण कार्य के लिए कितना कोड आवश्यक है। मेरी इच्छा है कि सी ++ में [स्थिर अगर] था (http://dlang.org/version.html#StaticIfCondition) :( –

2

एक समाधान आंशिक रूप से आपके वर्ग टेम्पलेट को विशेषज्ञता दे रहा है।

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 

     std::cout<<"non-void function"<<std::endl; 
     Ret returnVal; 
     return returnVal; 

    } 
}; 

template <typename... Args> 
class Test<void (Args...)> 
{ 
public: 
    void operator()(Args...) 
    { 

     std::cout<<"void function"<<std::endl; 
    } 
}; 


int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
}