यह दो प्रतिबंधों के साथ, इस तरह के एक विशेषता बनाने के लिए संभव है:
- संकलक के लिए, एक मुक्त समारोह कुछ मौलिक से अलग है एक वर्ग मज़ेदार जो
operator()
ओवरलोड करता है। इस प्रकार कार्यान्वयन करते समय हमें दोनों मामलों को अलग-अलग इलाज करना होगा। हालांकि यह उपयोग के लिए कोई समस्या नहीं है, हम उपयोगकर्ता से इस कार्यान्वयन विवरण को छुपा सकते हैं।
- हमें उस फ़ंक्शन के हस्ताक्षर को जानने की आवश्यकता है जिसे आप कॉल करना चाहते हैं। यह आम तौर पर एक समस्या नहीं है, और इसका अच्छा साइड इफेक्ट होता है कि हमारी विशेषता अतिव्यापी ओवरलोड को संभालने में सक्षम है।
चरण: नि: शुल्क कार्यों
के नि: शुल्क कार्यों के साथ शुरू करते हैं, क्योंकि वे छोटे हैं पता लगाने के लिए आसान करते हैं। हमारा कार्य है, जब एक फ़ंक्शन पॉइंटर दिया जाता है, यह निर्धारित करने के लिए कि उस फ़ंक्शन पॉइंटर का हस्ताक्षर दूसरे टेम्पलेट तर्क के रूप में पारित हस्ताक्षर से मेल खाता है या नहीं। उन लोगों की तुलना करने में सक्षम होने के लिए, हमें या तो अंतर्निहित फ़ंक्शन हस्ताक्षर को समझने की आवश्यकता है, या हमारे हस्ताक्षर का फ़ंक्शन पॉइंटर बनाएं। मैं मनमाने ढंग से उत्तरार्द्ध चुना है:
// 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
'is_function :: value' के बारे में कैसे? –
Fiktik
http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 आपके लिए रूचि हो सकता है। – pmr
क्या आप सिर्फ म्यूटर्स या किसी भी कॉल करने योग्य ऑब्जेक्ट के लिए परीक्षण करना चाहते हैं? ऐसा लगता है कि 'result_of' विशेषता का कुछ SFINAE उपयोग किसी भी कॉल करने योग्य प्रकार की पहचान करने के लिए काम करेगा। मैं थोड़ा आश्चर्यचकित हूं कि ऐसा लगता है कि पहले से ही कोई 'std :: is_callable' विशेषता नहीं है। – bames53