2017-07-25 23 views
7

मैं टेम्पलेट पैरामीटर में एक सदस्य समारोह baz() की उपस्थिति का पता लगाने की कोशिश कर रहा था:क्यों decltype (घोषणा <T>() .func()) काम करता है जहां decltype (& T :: func) नहीं है?

template<typename T, typename = void> 
struct ImplementsBaz : public std::false_type { }; 

template<typename T> 
struct ImplementsBaz<T, decltype(&T::baz)> : public std::true_type { }; 

लेकिन यह हमेशा झूठे पैदा करता है:

struct Foo {}; 
struct Bar { void baz() {} }; 

std::cout << ImplementsBaz<Foo>::value << std::endl; // 0 
std::cout << ImplementsBaz<Bar>::value << std::endl; // also 0 

declval और का उपयोग करना विधि काम करता है बुला, हालांकि:

template<typename T> 
struct ImplementsBaz<T, decltype(std::declval<T>().baz())> : public std::true_type { }; 

बेशक, अब यह ऑनल हो सकता है y 0 तर्कों के साथ baz फ़ंक्शन का पता लगाएं। declval<T>().baz() का उपयोग करते समय विशेषज्ञता का सही ढंग से चयन क्यों किया जाता है, लेकिन decltype(&T::baz) नहीं?

+0

बस एक अनुमान: यदि आप सामान्य टेम्पलेट में डिफ़ॉल्ट '' = void'' को हटाते हैं तो क्या होता है? – bjhend

+0

फिर 'कार्यान्वयन Baz :: मूल्य' अवैध है:' बहुत कम टेम्पलेट तर्क ' – jtbandes

+0

आह, हाँ, मैंने इसे अनदेखा कर दिया। – bjhend

उत्तर

3

आप void_t "का पता लगाने मुहावरा" का उपयोग करते हैं, तो यह काम के रूप में उम्मीद करता है: क्यों, this question विस्तार से बताती है

template <typename...> using void_t = void; 

template <typename T> 
struct ImplementsBaz<T, void_t<decltype(&T::baz)>> : std::true_type {}; 


struct Bar { void baz() {} }; 

static_assert(ImplementsBaz<Bar>::value); // passes 

Godbolt link

रूप करने के लिए कैसे "void_t चाल "काम करता है। स्वीकार्य उत्तर से उद्धरण के लिए:

ऐसा लगता है कि आपने has_member<A, void>::value लिखा था। अब, टेम्पलेट पैरामीटर सूची टेम्पलेट has_member के किसी भी विशेषज्ञता के मुकाबले तुलना की जाती है। केवल अगर कोई विशेषज्ञता मैच नहीं है, तो प्राथमिक टेम्पलेट की परिभाषा को फॉल-बैक के रूप में उपयोग किया जाता है।

मूल स्थिति में, decltype(&T::baz)void नहीं है, इसलिए विशेषज्ञता मूल टेम्पलेट से मेल नहीं खाता और इतने नहीं माना जाता। void को बदलने के लिए हमें void_t (या किसी अन्य तंत्र, जैसे कि कास्ट) का उपयोग करने की आवश्यकता है ताकि विशेषज्ञता का उपयोग किया जा सके।

+0

मुझे लगता है कि मुझे समझ में नहीं आ रहा है कि आंशिक विशेषज्ञता का उपयोग क्यों नहीं किया जाएगा - क्या आप जानते हैं कि किसी भी संसाधन के बारे में क्यों पता/जानते हैं? – jtbandes

+0

@jtbandes निश्चित रूप से, –

+0

संपादित करें देखें, धन्यवाद: _ "ऐसा लगता है जैसे आपने लिखा है 'है_मेम्बर :: मूल्य'। अब, टेम्पलेट पैरामीटर सूची टेम्पलेट के किसी भी विशेषज्ञता के मुकाबले तुलना की गई है। केवल अगर कोई विशेषज्ञता नहीं है मैचों, प्राथमिक टेम्पलेट की परिभाषा को फॉल-बैक के रूप में उपयोग किया जाता है। "_ – jtbandes

1

ऐसा इसलिए है क्योंकि decltype(&T::baz) एक त्रुटि है और आंशिक विशेषज्ञता कभी भी तत्काल नहीं होती है। T (यानी Bar) में baz नामक कोई स्थैतिक सदस्य नहीं है।

दूसरा व्यक्ति सही काम करता है, यानी एक उदाहरण पर विधि को कॉल करें और उसके बाद रिटर्न प्रकार का उपयोग करें।


तुम क्या आप इसे पारित करता है, तो वहाँ केवल एक अधिभार है मानकों की परवाह किए बिना विधि की उपस्थिति का पता करना चाहते हैं।

template <typename Type, typename = std::enable_if_t<true>> 
struct ImplementsBaz : public std::integral_constant<bool, true> {}; 
template <typename Type> 
struct ImplementsBaz<Type, std::enable_if_t< 
         std::is_same<decltype(&T::baz), decltype(&T::baz)> 
          ::value>> 
    : public std::integral_constant<bool, false> {}; 

आपको लगता है कि विधि की उपस्थिति का पता लगाने के लिए अगर यह भार के होते हैं चाहते हैं, member detection idiom पर एक नज़र डालें। असल में यह मानता है कि उस नाम के साथ एक विधि मौजूद है और फिर यदि उस नाम के साथ कोई और तरीका है तो गुण वर्ग त्रुटि में जाता है और सही true_type विशेषज्ञता या समान का चयन करता है। जरा देखो तो!

+0

मुझे लगता है कि आपका पहला जवाब वास्तव में सही था - ऐसा लगता है कि रिटर्न प्रकार मायने रखता है। 'और बार :: baz' वैध अभिव्यक्ति होना चाहिए, तो क्या' decltype (& T :: baz) 'का उपयोग करके ऐसा करने का कोई तरीका है? – jtbandes

+0

विवाद स्थिर रहता है भले ही बाज़ स्थिर हो। – jwimberley

+1

@jwimberley उस मामले में अभिव्यक्ति एक पते पर मूल्यांकन करती है, जो कि अभी भी 'void' – Curious

2

decltype(std::declval<T>().baz()) और

struct Bar { void baz() {} }; 

काम करता है क्योंकि baz() वापसी void तो void विशेष नहीं Implements_baz struct में डिफ़ॉल्ट typename = void अधिक मेल खाते हैं

decltype(&T::baz, void()) 

आपका उदाहरण के साथ प्रयास करें।

लेकिन जैसे

struct Bar { int baz() { return 0; } }; 

इस प्रकार यदि आप Bar परिभाषित आप से Implement_baz क्योंकि baz() वापसी intvoid से मेल नहीं खाता है कि false प्राप्त करते हैं।

decltype(&T::baz) के साथ समान समस्या: void से मेल नहीं खाता क्योंकि एक विधि के प्रकार को वापस कर देता है।

तो समाधान (अच्छी तरह से ...एक संभावित समाधान) decltype(&T::baz, void()) का उपयोग करें क्योंकि वापस T::baz मौजूद है (या विफल, और कुछ भी वापस नहीं, अगर T::baz मौजूद नहीं है)।

+0

पर एक नज़र डालें, यह शायद सबसे अच्छा है, जब तक कि उपयोगकर्ता केवल शून्य रिटर्न प्रकार के साथ कार्य T :: baz से मेल नहीं खाता। – jwimberley

+1

@jwimberley - एक अलग समस्या होगी; उस स्थिति में ओपी समाधान एक अच्छा समाधान है, लेकिन समस्या तब भी रहती है जो केवल तभी काम करती है जब 'baz()' को कोई तर्क नहीं मिलता है (या यदि डेवलपर तर्क के प्रकारों को जानता है)। – max66

0

एक अन्य संभावित समाधान

template<typename T> 
using BazResult = typename std::result_of<decltype(&T::baz)(T)>::type; 

template<typename T> 
struct ImplementsBaz<T, BazResult<T> > : public std::true_type { }; 

यह तभी यह सिर्फ आपका इरादा एक शून्य वापसी प्रकार के साथ काम करता है T::baz मैच के लिए कार्य करेगा जब आप पठनीयता के लिए पसंद करते हैं,

template<typename T> 
struct ImplementsBaz<T, typename std::result_of<decltype(&T::baz)(T)>::type > : public std::true_type { }; 

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

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