2012-01-31 10 views
12

यदि तर्क एक सी ++ फ़ंक्शन ऑब्जेक्ट (फ़ैक्टर) है तो मैं स्थिर रूप से कैसे कटौती कर सकता हूं?एक is_functor सी ++ विशेषता वर्ग संभव है?

template <typename F> 
void test(F f) {} 

मैंने is_function<F>::value की कोशिश की, लेकिन यह काम नहीं करता है। ऐसा लगता है कि is_functor विशेषता नहीं है, इसलिए शायद यह संभव नहीं है। मैं केवल एक विशिष्ट सदस्य फ़ंक्शन की तलाश में प्रतीत होता हूं, इस मामले में फ़ंक्शन कॉल ऑपरेटर: F::operator()

+0

'is_function :: value' के बारे में कैसे? – Fiktik

+1

http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 आपके लिए रूचि हो सकता है। – pmr

+1

क्या आप सिर्फ म्यूटर्स या किसी भी कॉल करने योग्य ऑब्जेक्ट के लिए परीक्षण करना चाहते हैं? ऐसा लगता है कि 'result_of' विशेषता का कुछ SFINAE उपयोग किसी भी कॉल करने योग्य प्रकार की पहचान करने के लिए काम करेगा। मैं थोड़ा आश्चर्यचकित हूं कि ऐसा लगता है कि पहले से ही कोई 'std :: is_callable' विशेषता नहीं है। – bames53

उत्तर

0
template<typename T, typename Sign>         
struct is_functor 
{                 
    typedef char yes[1];            
    typedef char no [2];            
    template <typename U, U> struct type_check;      
    template <typename _1> static yes &chk(type_check<Sign, &_1::operator()>*); 
    template <typename > static no &chk(...);      
    static bool const value = sizeof(chk<T>(nullptr)) == sizeof(yes);  
}; 

this answer से बदल दिया।

ऐसा लगता है कि इस्तेमाल किया जा सकता ...

template<typename T> 
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func() 
{ 
} 
+0

'टाइपनाम अस्वीकरण '? और यदि आपका ऑपरेटर ओवरलोड हो गया है तो आपका समाधान काम नहीं करता है। – kennytm

+0

फ़ैक्टर की आपकी परिभाषा अपूर्ण है। एक मानक मज़ेदार या तो एक फ़ंक्शन पॉइंटर या अधिभारित 'ऑपरेटर() 'वाला ऑब्जेक्ट होता है। –

+0

मैंने एक अलग समाधान पोस्ट किया; @MaximYegorushkin लेकिन नया इसके बारे में नहीं बदलता है, हमम – David

12

यह दो प्रतिबंधों के साथ, इस तरह के एक विशेषता बनाने के लिए संभव है:

  1. संकलक के लिए, एक मुक्त समारोह कुछ मौलिक से अलग है एक वर्ग मज़ेदार जो operator() ओवरलोड करता है। इस प्रकार कार्यान्वयन करते समय हमें दोनों मामलों को अलग-अलग इलाज करना होगा। हालांकि यह उपयोग के लिए कोई समस्या नहीं है, हम उपयोगकर्ता से इस कार्यान्वयन विवरण को छुपा सकते हैं।
  2. हमें उस फ़ंक्शन के हस्ताक्षर को जानने की आवश्यकता है जिसे आप कॉल करना चाहते हैं। यह आम तौर पर एक समस्या नहीं है, और इसका अच्छा साइड इफेक्ट होता है कि हमारी विशेषता अतिव्यापी ओवरलोड को संभालने में सक्षम है।

चरण: नि: शुल्क कार्यों

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

// build R (*)(Args...) from R (Args...) 
// compile error if signature is not a valid function signature 
template <typename, typename> 
struct build_free_function; 

template <typename F, typename R, typename ... Args> 
struct build_free_function<F, R (Args...)> 
{ using type = R (*)(Args...); }; 

अब वह सब करना बचा है तुलना करने के लिए है और हम मुक्त समारोह भाग के साथ किया जाता है:

// determine whether a free function pointer F has signature S 
template <typename F, typename S> 
struct is_function_with_signature 
{ 
    // check whether F and the function pointer of S are of the same 
    // type 
    static bool constexpr value = std::is_same< 
     F, typename build_free_function<F, S>::type 
    >::value; 
}; 

चरण दो: कक्षा functors

यह एक और अधिक शामिल है। हम आसानी से SFINAE साथ पता लगा सकता है एक वर्ग एक operator() को परिभाषित करता है कि क्या:

template <typename T> 
struct defines_functor_operator 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    // we need a template here to enable SFINAE 
    template <typename U> 
    static yes deduce(char (*)[sizeof(&U::operator())]); 
    // fallback 
    template <typename> static no deduce(...); 

    static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes); 
}; 

लेकिन यह है कि हमें बताओ नहीं है हमारे वांछित समारोह हस्ताक्षर के लिए एक मौजूद है या नहीं! सौभाग्य से, हम यहां एक चाल का उपयोग कर सकते हैं: पॉइंटर्स वैध टेम्पलेट पैरामीटर हैं। इस प्रकार हम पहले हमारे वांछित हस्ताक्षर के सदस्य समारोह सूचक का उपयोग करें और देखें कि क्या &T::operator() उस प्रकार के है कर सकते हैं:

template <typename T, T> struct check; 

अब check<void (C::*)() const, &C::operator()> केवल एक वैध टेम्पलेट इन्स्टेन्शियशन हो जाएगा करता है तो C वास्तव में एक void C::operator()() const है। लेकिन ऐसा करने के लिए हमें पहले C और सदस्य फ़ंक्शन पॉइंटर के हस्ताक्षर को जोड़ना होगा। जैसा कि हमने पहले ही देखा है, हमें दो अतिरिक्त मामलों की चिंता करने की आवश्यकता है जिन्हें हमें मुफ्त कार्यों के बारे में परवाह नहीं है: const और volatile फ़ंक्शंस।इसके अलावा कि यह बहुत ज्यादा एक ही है:

// build R (C::*)(Args...) from R (Args...) 
//  R (C::*)(Args...) const from R (Args...) const 
//  R (C::*)(Args...) volatile from R (Args...) volatile 
// compile error if signature is not a valid member function signature 
template <typename, typename> 
struct build_class_function; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...)> 
{ using type = R (C::*)(Args...); }; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...) const> 
{ using type = R (C::*)(Args...) const; }; 

template <typename C, typename R, typename ... Args> 
struct build_class_function<C, R (Args...) volatile> 
{ using type = R (C::*)(Args...) volatile; }; 

कि लाना और हमारे निष्कर्ष एक साथ check सहायक struct के विषय में, हम functor वस्तुओं के लिए हमारी जांच metafunction मिलती है:

// determine whether a class C has an operator() with signature S 
template <typename C, typename S> 
struct is_functor_with_signature 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    // helper struct to determine that C::operator() does indeed have 
    // the desired signature; &C::operator() is only of type 
    // R (C::*)(Args...) if this is true 
    template <typename T, T> struct check; 

    // T is needed to enable SFINAE 
    template <typename T> static yes deduce(check< 
     typename build_class_function<C, S>::type, &T::operator()> *); 
    // fallback if check helper could not be built 
    template <typename> static no deduce(...); 

    static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes); 
}; 

चरण तीन: टुकड़े लाना एक साथ

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

// C is a class, delegate to is_functor_with_signature 
template <typename C, typename S, bool> 
struct is_callable_impl 
    : std::integral_constant< 
     bool, is_functor_with_signature<C, S>::value 
     > 
{}; 

// F is not a class, delegate to is_function_with_signature 
template <typename F, typename S> 
struct is_callable_impl<F, S, false> 
    : std::integral_constant< 
     bool, is_function_with_signature<F, S>::value 
     > 
{}; 

तो हम अंत में पहेली के अंतिम टुकड़ा, जोड़ सकते हैं हमारी वास्तविक is_callable विशेषता जा रहा है:: तो हम सभी यह करना है एक बूलियन पैरामीटर पर विशेषज्ञ है

// Determine whether type Callable is callable with signature Signature. 
// Compliant with functors, i.e. classes that declare operator(); and free 
// function pointers: R (*)(Args...), but not R (Args...)! 
template <typename Callable, typename Signature> 
struct is_callable 
    : is_callable_impl< 
     Callable, Signature, 
     std::is_class<Callable>::value 
     > 
{}; 

अब हम को साफ हमारे कोड, कार्यान्वयन विवरण को अज्ञात नेमस्पेस में रखें ताकि वे हमारी फ़ाइल के बाहर स्वीकार्य न हों, और हमारे प्रोजेक्ट में उपयोग करने के लिए is_callable.hpp अच्छा लगा।

पूर्ण कोड

namespace // implementation detail 
{ 
    // build R (*)(Args...) from R (Args...) 
    // compile error if signature is not a valid function signature 
    template <typename, typename> 
    struct build_free_function; 

    template <typename F, typename R, typename ... Args> 
    struct build_free_function<F, R (Args...)> 
    { using type = R (*)(Args...); }; 

    // build R (C::*)(Args...) from R (Args...) 
    //  R (C::*)(Args...) const from R (Args...) const 
    //  R (C::*)(Args...) volatile from R (Args...) volatile 
    // compile error if signature is not a valid member function signature 
    template <typename, typename> 
    struct build_class_function; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...)> 
    { using type = R (C::*)(Args...); }; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...) const> 
    { using type = R (C::*)(Args...) const; }; 

    template <typename C, typename R, typename ... Args> 
    struct build_class_function<C, R (Args...) volatile> 
    { using type = R (C::*)(Args...) volatile; }; 

    // determine whether a class C has an operator() with signature S 
    template <typename C, typename S> 
    struct is_functor_with_signature 
    { 
     typedef char (& yes)[1]; 
     typedef char (& no)[2]; 

     // helper struct to determine that C::operator() does indeed have 
     // the desired signature; &C::operator() is only of type 
     // R (C::*)(Args...) if this is true 
     template <typename T, T> struct check; 

     // T is needed to enable SFINAE 
     template <typename T> static yes deduce(check< 
      typename build_class_function<C, S>::type, &T::operator()> *); 
     // fallback if check helper could not be built 
     template <typename> static no deduce(...); 

     static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes); 
    }; 

    // determine whether a free function pointer F has signature S 
    template <typename F, typename S> 
    struct is_function_with_signature 
    { 
     // check whether F and the function pointer of S are of the same 
     // type 
     static bool constexpr value = std::is_same< 
      F, typename build_free_function<F, S>::type 
     >::value; 
    }; 

    // C is a class, delegate to is_functor_with_signature 
    template <typename C, typename S, bool> 
    struct is_callable_impl 
     : std::integral_constant< 
      bool, is_functor_with_signature<C, S>::value 
      > 
    {}; 

    // F is not a class, delegate to is_function_with_signature 
    template <typename F, typename S> 
    struct is_callable_impl<F, S, false> 
     : std::integral_constant< 
      bool, is_function_with_signature<F, S>::value 
      > 
    {}; 
} 

// Determine whether type Callable is callable with signature Signature. 
// Compliant with functors, i.e. classes that declare operator(); and free 
// function pointers: R (*)(Args...), but not R (Args...)! 
template <typename Callable, typename Signature> 
struct is_callable 
    : is_callable_impl< 
     Callable, Signature, 
     std::is_class<Callable>::value 
     > 
{}; 

Ideone कुछ परीक्षण

http://ideone.com/7PWdiv

+0

वाह.वाह.वाह.वाह.वाह। – mark

0

हालांकि इस अतिभारित काम करता है, अन्य सभी मामलों के लिए (मुक्त काम करता है, वर्गों को लागू करने के लिए काम नहीं करता है के साथ उदाहरण operator(), और लैम्बडास) यह संक्षिप्त समाधान सी ++ 11:

में काम करता है
template <typename T, typename Signature> 
struct is_callable: std::is_convertible<T,std::function<Signature>> { }; 

नोट: std::is_callable सी ++ 17 में उपलब्ध होगा।

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