वर्ग सदस्य फ़ंक्शन के अस्तित्व के लिए अस्थायी जाँच?


497

क्या यह संभव है कि एक निश्चित सदस्य फ़ंक्शन को किसी वर्ग पर परिभाषित करने के आधार पर व्यवहार में परिवर्तन करने वाला टेम्पलेट लिखना संभव हो?

यहाँ एक सरल उदाहरण है कि मैं क्या लिखना चाहता हूँ:

template<class T>
std::string optionalToString(T* obj)
{
    if (FUNCTION_EXISTS(T->toString))
        return obj->toString();
    else
        return "toString not defined";
}

तो, अगर परिभाषित class Tकिया toString()गया है, तो यह इसका उपयोग करता है; अन्यथा, यह नहीं है। जादुई हिस्सा जो मुझे नहीं पता कि कैसे करना है "FUNCTION_EXISTS" भाग।


6
निश्चित रूप से यह बिना कहे चला जाता है कि नीचे दिए गए टेम्पलेट उत्तर (एस) केवल संकलन-समय की जानकारी के साथ काम करते हैं, अर्थात टी को स्ट्रींग करना होगा। यदि आप T के एक उपवर्ग में गुजरते हैं जो String को परिभाषित करता है , लेकिन T नहीं करता है , तो आपको बताया जाएगा कि String को परिभाषित नहीं किया गया है।
एलिस पर्ससेल

जवाबों:


319

हां, SFINAE से आप यह देख सकते हैं कि क्या एक दिया गया वर्ग एक निश्चित विधि प्रदान करता है। यहाँ काम कर कोड है:

#include <iostream>

struct Hello
{
    int helloworld() { return 0; }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two { char x[2]; };

    template <typename C> static one test( typeof(&C::helloworld) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
    std::cout << has_helloworld<Hello>::value << std::endl;
    std::cout << has_helloworld<Generic>::value << std::endl;
    return 0;
}

मैंने अभी-अभी इसे लिनक्स के साथ टेस्ट किया है और 4.1 / 4.3 के साथ। मुझे नहीं पता कि यह विभिन्न कंपाइलरों को चलाने वाले अन्य प्लेटफार्मों के लिए पोर्टेबल है या नहीं।


18
हालांकि, मैंने 'एक' और 'दो' के लिए निम्नलिखित का उपयोग किया: टाइपडैफ चार स्माल; प्लेटफ़ॉर्म आश्रित चर आकार के बारे में कोई अस्पष्टता सुनिश्चित करने के लिए वर्ग बिग {चार डमी [2];}।
user23167

6
मुझे संदेह है कि यह पृथ्वी के आकार (चार) == आकार (लंबे) के साथ एक मंच पर मौजूद है
निकोला बोनेली

17
मुझे पूरा यकीन नहीं है, लेकिन मुझे नहीं लगता कि यह पोर्टेबल है। टाइपोफ़ एक जीसीसी विस्तार है, यह अन्य संकलक पर काम नहीं करेगा।
लियोन टिम्मरमैन

56
टाइपोफ़ की जरूरत नहीं है - चार [आकार (और सी :: हेलोवर्ल्ड)] भी काम करता है। और आकार (लंबे) == आकार (चार) से बचने के लिए, एक संरचना {चार [2]};) का उपयोग करें; इसका आकार> = 2
MSalters

57
बदलें: तुच्छ है, लेकिन मेरे एक समय यह पता लगाने की ले ली typeofद्वारा decltypeउपयोग करते समय C ++ 0x , जैसे, -std = c ++ 0x के माध्यम से।
हृदय दरआर

264

यह प्रश्न पुराना है, लेकिन C ++ 11 के साथ हमें एक कार्य अस्तित्व (या किसी भी गैर-प्रकार के सदस्य के अस्तित्व की जांच करने का एक नया तरीका मिला है, वास्तव में), फिर से SFINAE पर निर्भर है:

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
    -> decltype(os << obj, void())
{
  os << obj;
}

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
    -> decltype(obj.stream(os), void())
{
  obj.stream(os);
}

template<class T>
auto serialize(std::ostream& os, T const& obj)
    -> decltype(serialize_imp(os, obj, 0), void())
{
  serialize_imp(os, obj, 0);
}

अब कुछ स्पष्टीकरण पर। पहली बात, मैं अधिभार संकल्प से कार्यों को बाहर करने के लिए अभिव्यक्ति SFINAE का उपयोग करता हूं serialize(_imp), अगर अंदर पहली अभिव्यक्ति decltypeवैध नहीं है (उर्फ, फ़ंक्शन मौजूद नहीं है)।

void()उन सभी कार्यों की वापसी प्रकार बनाने के लिए प्रयोग किया जाता है void

0तर्क पसंद किया जाता है os << objअधिभार अगर दोनों उपलब्ध हैं (शाब्दिक 0प्रकार का है intऔर इस तरह के पहले अधिभार के रूप में एक बेहतर मुकाबला नहीं है)।


अब, आप शायद यह देखना चाहते हैं कि कोई फ़ंक्शन मौजूद है या नहीं। सौभाग्य से, यह लिखना आसान है। ध्यान दें, हालांकि, आपको अपने हर अलग-अलग फ़ंक्शन नाम के लिए एक विशेषता लिखने की आवश्यकता है जो आप चाहते हैं।

#include <type_traits>

template<class>
struct sfinae_true : std::true_type{};

namespace detail{
  template<class T, class A0>
  static auto test_stream(int)
      -> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
  template<class, class A0>
  static auto test_stream(long) -> std::false_type;
} // detail::

template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};

जीवंत उदाहरण।

और स्पष्टीकरण पर। सबसे पहले, sfinae_trueएक सहायक प्रकार है, और यह मूल रूप से लेखन के समान है decltype(void(std::declval<T>().stream(a0)), std::true_type{})। लाभ यह है कि यह छोटा है।
अगला, या struct has_stream : decltype(...)तो अंत से विरासत में मिलता है , इस पर निर्भर करता है कि चेक विफल रहता है या नहीं। अंतिम, आपको जो भी प्रकार से गुजरता है, उसका "मूल्य" देता है , आपको यह जानने की आवश्यकता के बिना कि आप इसका निर्माण कैसे कर सकते हैं। ध्यान दें कि यह केवल एक अज्ञात संदर्भ के अंदर संभव है, जैसे कि , और अन्य।std::true_typestd::false_typedecltypetest_stream
std::declvaldecltypesizeof


ध्यान दें कि decltypeआवश्यक नहीं है, के रूप में sizeof(और सभी unevaluated संदर्भों) कि वृद्धि हुई है। यह सिर्फ इतना है कि decltypeपहले से ही एक प्रकार बचाता है और इस तरह के रूप में सिर्फ क्लीनर है। यहाँ एक sizeofअधिभार का एक संस्करण है:

template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
    int(*)[sizeof((os << obj),0)] = 0)
{
  os << obj;
}

intऔर longमानकों को एक ही कारण के लिए वहाँ अभी भी कर रहे हैं। सरणी पॉइंटर का उपयोग एक संदर्भ प्रदान करने के लिए किया जाता है जहां sizeofइसका उपयोग किया जा सकता है।


4
decltypeओवर sizeofका लाभ यह भी है कि फ़ंक्शन कॉल के लिए विशेष रूप से तैयार किए गए नियमों द्वारा एक अस्थायी नहीं पेश किया जाता है (इसलिए आपके पास वापसी प्रकार के विध्वंसक तक पहुंच अधिकार नहीं है और यदि वापसी प्रकार है, तो एक अंतर्निहित तात्कालिकता का कारण नहीं होगा। एक कक्षा टेम्पलेट तात्कालिकता)।
जोहान्स शाउब -

5
Microsoft ने C ++ संकलक में अभी तक अभिव्यक्ति SFINAE को लागू नहीं किया है। बस आंकड़ा मैं कुछ लोगों को समय बचाने में मदद कर सकता हूं, क्योंकि मैं उलझन में था कि यह मेरे लिए काम क्यों नहीं कर रहा है। अच्छा समाधान हालांकि, यह दृश्य स्टूडियो में उपयोग करने के लिए इंतजार नहीं कर सकता!
जोनाथन

3
आपका पहला उदाहरण लिंक टूट गया है
नाथनऑलवर

1
यह कहा जाना चाहिए, कि static_assert(has_stream<X, char>() == true, "fail X");संकलन करेगा और जोर नहीं देगा क्योंकि चार इंट में परिवर्तनीय है, इसलिए यदि वह व्यवहार नहीं चाहता है और चाहता है कि सभी तर्क प्रकार मैच से मैं नहीं जानता कि इसे कैसे प्राप्त किया जा सकता है?
गेब्रियल

4
यदि आप उतने ही हैरान हैं जितने कि मैं घोषणा करने के लिए दो तर्कों पर था: घोषणापत्र वास्तव में केवल एक ही लेता है; अल्पविराम यहाँ एक ऑपरेटर है। देखें stackoverflow.com/questions/16044514/...
आन्द्रे

159

C ++ इसके लिए SFINAE का उपयोग करने की अनुमति देता है (ध्यान दें कि C ++ 11 विशेषताओं के साथ यह सरल है क्योंकि यह लगभग मनमाने ढंग से अभिव्यक्तियों पर विस्तारित SFINAE का समर्थन करता है - नीचे सामान्य C ++ 03 संकलक के साथ काम करने के लिए तैयार किया गया था):

#define HAS_MEM_FUNC(func, name)                                        \
    template<typename T, typename Sign>                                 \
    struct name {                                                       \
        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::func > *); \
        template <typename   > static no  &chk(...);                    \
        static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     \
    }

उपर्युक्त टेम्पलेट और मैक्रो एक टेम्पलेट को त्वरित करने की कोशिश करता है, जिससे यह एक सदस्य फ़ंक्शन पॉइंटर प्रकार और वास्तविक सदस्य फ़ंक्शन पॉइंटर देता है। यदि फिट न होने के प्रकार, SFINAE टेम्पलेट को नजरअंदाज कर देता है। इस तरह का उपयोग:

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> void
doSomething() {
   if(has_to_string<T, std::string(T::*)()>::value) {
      ...
   } else {
      ...
   }
}

लेकिन ध्यान दें कि आप उस toStringफ़ंक्शन को उस शाखा में नहीं कह सकते । चूंकि संकलक दोनों शाखाओं में वैधता के लिए जाँच करेगा, जो कि फ़ंक्शन के मौजूद न होने के मामलों के लिए विफल होगा। एक बार फिर से SFINAE का उपयोग करने का एक तरीका है (enable_if को बढ़ावा से भी प्राप्त किया जा सकता है):

template<bool C, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> { };

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> 
typename enable_if<has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T has toString ... */
   return t->toString();
}

template<typename T> 
typename enable_if<!has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T doesnt have toString ... */
   return "T::toString() does not exist.";
}

इसका उपयोग करने में मज़ा आता है। इसका लाभ यह है कि यह ओवरलोड सदस्य कार्यों के लिए भी काम करता है, और कॉन्स्ट सदस्य कार्यों के लिए भी ( std::string(T::*)() constसदस्य फ़ंक्शन सूचक प्रकार के रूप में उपयोग करते हुए याद रखें !)।


7
मुझे पसंद है कि type_checkयह सुनिश्चित करने के लिए कैसे उपयोग किया जाता है कि हस्ताक्षर बिल्कुल सहमत हैं। क्या इसे बनाने का कोई तरीका है ताकि यह किसी भी विधि से मेल खाए जिसे उस तरीके Signसे बुलाया जा सकता है जिसे हस्ताक्षर के साथ विधि कहा जा सकता है? (उदाहरण के लिए Sign= std::string(T::*)(), std::string T::toString(int default = 42, ...)मैच के लिए अनुमति दें ।)
j_random_hacker

5
मैं सिर्फ इस बारे में कुछ पता लगाता हूं जो मेरे लिए तत्काल स्पष्ट नहीं था, इसलिए यदि यह दूसरों की मदद करता है: chk नहीं है और इसे परिभाषित करने की आवश्यकता नहीं है! आकारऑफ़ ऑपरेटर को chk के आउटपुट के आकार को निर्धारित करता है बिना chk को कभी भी कॉल करने की आवश्यकता नहीं होती है।
SCFrench

3
@ deek0146: हाँ, Tएक आदिम प्रकार नहीं होना चाहिए, क्योंकि सूचक-टू-मेथड-ऑफ-टी घोषणा SFINAE के अधीन नहीं है और किसी भी गैर-वर्ग टी के लिए त्रुटि करेगा। IMO सबसे आसान उपाय यह है कि is_classचेक से गठबंधन करें बढ़ावा।
जन हुदेक

2
यदि मेरा toStringअस्थायी कार्य है तो मैं यह कार्य कैसे कर सकता हूं ?
फ्रैंक

4
क्या बूस्ट में यह (या कुछ भी समतुल्य) है?
दान निसेनबाम

89

सी ++ 20 - requiresभाव

C ++ 20 के साथ अवधारणाएं और मिश्रित उपकरण जैसे कि requiresअभिव्यक्ति जो एक फ़ंक्शन अस्तित्व की जांच करने के लिए एक अंतर्निहित तरीका है। उनके साथ आप अपने optionalToStringकार्य को फिर से लिख सकते हैं:

template<class T>
std::string optionalToString(T* obj)
{
    constexpr bool has_toString = requires(const T& t) {
        t.toString();
    };

    if constexpr (has_toString)
        return obj->toString();
    else
        return "toString not defined";
}

प्री-सी ++ 20 - डिटेक्शन टूलकिट

N4502 ने C ++ 17 मानक लाइब्रेरी में शामिल करने के लिए एक डिटेक्शन टूलकिट का प्रस्ताव रखा है जो अंततः इसे लाइब्रेरी फंडामेंटल्स TS v2 में बनाया। यह सबसे अधिक संभावना है कि यह कभी भी मानक में नहीं आएगा क्योंकि यह तब से requiresअभिव्यक्त हो गया है, लेकिन यह अभी भी समस्या को कुछ हद तक हल करता है। टूलकिट कुछ मेटाफैक्शन्स का परिचय देता है, जिनमें std::is_detectedसे इसके ऊपर टाइप या फंक्शन डिटेक्शन मेटाफ़ंक्शन को आसानी से लिखने के लिए इस्तेमाल किया जा सकता है। यहाँ आप इसका उपयोग कैसे कर सकते हैं:

template<typename T>
using toString_t = decltype( std::declval<T&>().toString() );

template<typename T>
constexpr bool has_toString = std::is_detected_v<toString_t, T>;

ध्यान दें कि ऊपर दिया गया उदाहरण अप्रयुक्त है। डिटेक्शन टूलकिट अभी तक मानक पुस्तकालयों में उपलब्ध नहीं है, लेकिन प्रस्ताव में एक पूर्ण कार्यान्वयन शामिल है जिसे आप आसानी से कॉपी कर सकते हैं यदि आपको वास्तव में इसकी आवश्यकता है। यह C ++ 17 फीचर के साथ अच्छा खेलता है if constexpr:

template<class T>
std::string optionalToString(T* obj)
{
    if constexpr (has_toString<T>)
        return obj->toString();
    else
        return "toString not defined";
}

सी ++ 14 - बूस्ट.हाना

Boost.Hana जाहिरा तौर पर इस विशिष्ट उदाहरण का निर्माण करता है और इसके प्रलेखन में C ++ 14 के लिए एक समाधान प्रदान करता है, इसलिए मैं इसे सीधे उद्धृत करने जा रहा हूं:

[...] हाना एक is_validफ़ंक्शन प्रदान करता है जिसे C ++ 14 जेनेरिक लैम्ब्डा के साथ जोड़ा जा सकता है ताकि एक ही क्लीनर का बहुत अधिक कार्यान्वयन हो सके:

auto has_toString = hana::is_valid([](auto&& obj) -> decltype(obj.toString()) { });

यह हमें एक फ़ंक्शन ऑब्जेक्ट के साथ छोड़ देता है has_toStringजो यह देता है कि क्या दी गई अभिव्यक्ति उस तर्क पर मान्य है जो हम इसे पास करते हैं। परिणाम को एक के रूप में वापस किया जाता है IntegralConstant, इसलिए कॉन्स्टैक्स-नेस यहां कोई मुद्दा नहीं है क्योंकि फ़ंक्शन के परिणाम को वैसे भी एक प्रकार के रूप में दर्शाया गया है। अब, कम क्रिया होने के अलावा (यह एक लाइनर है!), आशय बहुत स्पष्ट है। अन्य लाभ तथ्य यह है कि has_toStringउच्च क्रम एल्गोरिदम को पारित किया जा सकता है और इसे फ़ंक्शन दायरे में भी परिभाषित किया जा सकता है, इसलिए कार्यान्वयन विवरण के साथ नाम स्थान के दायरे को प्रदूषित करने की आवश्यकता नहीं है।

Boost.TTI

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

#include <boost/tti/has_member_function.hpp>

// Generate the metafunction
BOOST_TTI_HAS_MEMBER_FUNCTION(toString)

// Check whether T has a member function toString
// which takes no parameter and returns a std::string
constexpr bool foo = has_member_function_toString<T, std::string>::value;

फिर, आप boolएक SFINAE चेक बनाने के लिए उपयोग कर सकते हैं ।

व्याख्या

मैक्रो BOOST_TTI_HAS_MEMBER_FUNCTIONमेटाफ़ंक्शन उत्पन्न करता है has_member_function_toStringजो जाँच प्रकार को अपने पहले टेम्पलेट पैरामीटर के रूप में लेता है। दूसरा टेम्पलेट पैरामीटर सदस्य प्रकार के रिटर्न प्रकार से मेल खाता है, और निम्न पैरामीटर फ़ंक्शन के मापदंडों के प्रकार के अनुरूप हैं। यदि सदस्य valueमें कोई फ़ंक्शन फ़ंक्शन है true, तो सदस्य शामिल Tहै std::string toString()

वैकल्पिक रूप से, has_member_function_toStringटेम्पलेट पैरामीटर के रूप में एक सदस्य फ़ंक्शन पॉइंटर ले सकते हैं। इसलिए, इसे बदलना संभव has_member_function_toString<T, std::string>::valueहै has_member_function_toString<std::string T::* ()>::value


1
03 से अधिक संक्षिप्त
ZFY

@ZFY मुझे लगता है कि Boost.TTI C ++ 03 के साथ भी काम करता है, लेकिन यह बहुत से कम से कम सुरुचिपूर्ण समाधान है।
मोरवेन

क्या C ++ 20 समाधान वास्तव में मान्य है? मैं इसे पसंद करूंगा - लेकिन इसे g ++ और msvc द्वारा अस्वीकार कर दिया गया है - केवल क्लैंग द्वारा स्वीकार किया गया है।
बरंड बुमन्स

cppreference पर आप पढ़ सकते हैं: यदि किसी आवश्यकता-अभिव्यक्ति में उसकी आवश्यकताओं में अमान्य प्रकार या अभिव्यक्तियाँ शामिल हैं, और यह एक टेम्पर्ड इकाई की घोषणा के भीतर प्रकट नहीं होता है, तो यह प्रोग्राम बीमार है।
बरंड बुमन्स

@BerndBaumanns सच में? मुझे यह GCC ट्रंक के साथ काम करने के लिए मिला: godbolt.org/z/CBwZdE हो सकता है कि आप सही हों, मैंने केवल जाँच की थी कि यह काम किया है, लेकिन यह जाँच नहीं किया है कि यह मानक शब्दांकन के अनुसार कानूनी था या नहीं।
मोरविन

56

हालांकि यह सवाल दो साल पुराना है, लेकिन मैं अपना जवाब जोड़ने की हिम्मत करूंगा। उम्मीद है कि यह पिछले, निर्विवाद रूप से उत्कृष्ट, समाधान को स्पष्ट करेगा। मैंने निकोला बोनेली और जोहानस शाउब के बहुत ही उपयोगी उत्तर लिए और उन्हें एक समाधान में मिला दिया जो कि, IMHO, अधिक पठनीय, स्पष्ट और typeofविस्तार की आवश्यकता नहीं है :

template <class Type>
class TypeHasToString
{
    // This type won't compile if the second template parameter isn't of type T,
    // so I can put a function pointer type in the first parameter and the function
    // itself in the second thus checking that the function has a specific signature.
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef long No;

    // A helper struct to hold the declaration of the function pointer.
    // Change it if the function signature changes.
    template <typename T> struct ToString
    {
        typedef void (T::*fptr)();
    };

    template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
    template <typename T> static No  HasToString(...);

public:
    static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};

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


1
बस सोच रहा था, यह कुछ भी करता है जो कोनराड रूडोल्फ का समाधान नीचे नहीं करता है?
एलिस्टेयर इरविन

3
@AlastairIrvine, यह समाधान अंदर सभी तर्क को छुपाता है, कोनराड उपयोगकर्ता पर कुछ बोझ डालता है। यद्यपि कम और बहुत अधिक पठनीय, कोनराड के समाधान के लिए प्रत्येक वर्ग के लिए एक अलग टेम्पलेट विशेषज्ञता की आवश्यकता होती है toString। यदि आप एक जेनेरिक लाइब्रेरी लिखते हैं, तो वहां किसी भी वर्ग के साथ काम करने की इच्छा होती है (किसी चीज़ को बढ़ावा देने के बारे में सोचें), तो उपयोगकर्ता को कुछ अस्पष्ट टेम्प्लेट की अतिरिक्त विशेषज्ञता को परिभाषित करने की आवश्यकता अस्वीकार्य हो सकती है। कभी-कभी सार्वजनिक इंटरफ़ेस को सरल रखने के लिए एक बहुत ही जटिल कोड लिखना बेहतर होता है क्योंकि यह हो सकता है।
फायरऑफिस

30

C ++ 11 के लिए एक सरल समाधान:

template<class T>
auto optionalToString(T* obj)
 -> decltype(  obj->toString()  )
{
    return     obj->toString();
}
auto optionalToString(...) -> string
{
    return "toString not defined";
}

अद्यतन, 3 साल बाद: (और यह अप्राप्त है)। अस्तित्व के लिए परीक्षण करने के लिए, मुझे लगता है कि यह काम करेगा:

template<class T>
constexpr auto test_has_toString_method(T* obj)
 -> decltype(  obj->toString() , std::true_type{} )
{
    return     obj->toString();
}
constexpr auto test_has_toString_method(...) -> std::false_type
{
    return "toString not defined";
}

4
यह सरल और सुरुचिपूर्ण है, लेकिन सख्ती से बोलना ओपी के सवाल का जवाब नहीं देता है: आप फोन करने वाले को फ़ंक्शन के अस्तित्व की जांच करने में सक्षम नहीं करते हैं, आप हमेशा इसे प्रदान करते हैं। लेकिन वैसे भी अच्छा है।
एड्रियन डब्ल्यू

@ AdrianW, अच्छी बात है। मैंने अपना उत्तर अपडेट कर दिया है। हालांकि मैंने इसका परीक्षण नहीं किया है
हारून मैकडैड

यदि यह किसी और की मदद करता है, तो मैं इस काम को बिना template<typename>वैरिएड अधिभार के पहले नहीं कर सकता था: यह संकल्प के लिए विचार नहीं किया जा रहा था।
लेबरटोरियो कोबोटिका

फिर, यह अमान्य है C ++ 11।
पीटर

29

यह किस प्रकार के लक्षण हैं। दुर्भाग्य से, उन्हें मैन्युअल रूप से परिभाषित किया जाना है। अपने मामले में, निम्नलिखित की कल्पना करें:

template <typename T>
struct response_trait {
    static bool const has_tostring = false;
};

template <>
struct response_trait<your_type_with_tostring> {
    static bool const has_tostring = true;
}

5
आपको स्थैतिक स्थिरांक के बजाय लक्षणों के लिए enum पसंद करना चाहिए: "स्टेटिक स्थिर सदस्य lvalues ​​हैं, जो संकलक को तत्काल सदस्य के लिए परिभाषा को आवंटित करने और बाध्य करने के लिए मजबूर करता है। नतीजतन, गणना अब एक शुद्ध - संकलन-समय तक सीमित नहीं है। " प्रभाव।"
9zgür

5
"एन्युमरेशन वैल्यूज़ लैवल्यूज़ नहीं हैं (अर्थात, उनके पास कोई पता नहीं है)।, जब आप उन्हें पास करते हैं" तो संदर्भ के अनुसार, "कोई स्टैटिक मेमोरी का उपयोग नहीं किया जाता है। यह लगभग वैसा ही है जैसे कि आपने एक लीटर के रूप में गणना की गई वैल्यू पास की हो। । ये विचार हमें गणना मूल्यों "सी ++ टेम्पलेट्स: पूर्ण गाइड
gzgür

22
Comptrol: नहीं, उद्धृत अंश यहां लागू नहीं होता है क्योंकि पूर्णांक प्रकार स्थिर स्थिरांक एक विशेष मामला है! वे व्यवहार करते हैं वास्तव में एक enum यहाँ की तरह और पसंदीदा तरीका है। पुराने एनम हैक केवल कंपाइलरों पर आवश्यक थे जो C ++ मानक का पालन नहीं करते थे।
कोनराड रुडोल्फ

3
@ रेंजर पाट: बिलकुल नहीं। "कार्यक्रम में उपयोग किया जाता है" यहाँ स्पष्ट रूप से "संदर्भित" का पर्याय है। इस मार्ग के प्रचलित पढ़ने, और सभी आधुनिक C ++ कंपाइलरों द्वारा कार्यान्वित, यह है कि आप इसे घोषित करने की आवश्यकता के बिना एक स्थिर स्थिर का मान ले सकते हैं (पिछले वाक्य यह कहते हैं: “… सदस्य अभिन्न निरंतर अभिव्यक्तियों में प्रकट हो सकता है ... ")। आपको केवल इसे परिभाषित करने की आवश्यकता है यदि आप इसका पता लेते हैं (स्पष्ट रूप से इसके माध्यम से &T::xया संदर्भ में इसे बांधकर)।
कोनराड रुडोल्फ


25

वैसे, इस सवाल के जवाबों की एक लंबी सूची पहले से ही है, लेकिन मैं मॉर्वेन की टिप्पणी पर जोर देना चाहूंगा: सी ++ 17 के लिए एक प्रस्ताव है जो इसे वास्तव में बहुत सरल बनाता है। देखें N4502 जानकारी के लिए, लेकिन एक आत्म निहित उदाहरण के रूप में निम्नलिखित पर विचार करें।

यह हिस्सा निरंतर भाग है, इसे हेडर में रखें।

// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;

// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};

// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};

फिर चर हिस्सा है, जहां आप निर्दिष्ट करते हैं कि आप क्या देख रहे हैं (एक प्रकार, एक सदस्य प्रकार, एक फ़ंक्शन, एक सदस्य फ़ंक्शन आदि)। ओपी के मामले में:

template <typename T>
using toString_t = decltype(std::declval<T>().toString());

template <typename T>
using has_toString = detect<T, toString_t>;

निम्नलिखित उदाहरण, N4502 से लिया गया , एक अधिक विस्तृत जांच दिखाता है:

// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())

// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;

ऊपर वर्णित अन्य कार्यान्वयनों की तुलना में, यह एक काफी सरल है: उपकरण का एक कम सेट ( void_tऔर detect) पर्याप्त है, बालों वाले मैक्रोज़ की कोई आवश्यकता नहीं है। इसके अलावा, यह बताया गया था ( N4502 देखें ) कि यह पिछले दृष्टिकोणों के अनुसार औसत रूप से अधिक कुशल (संकलन-समय और संकलक मेमोरी की खपत) है।

यहाँ एक जीवंत उदाहरण है । यह क्लैंग के साथ ठीक काम करता है, लेकिन दुर्भाग्य से, 5.1 से पहले के जीसीसी संस्करणों ने सी ++ 11 मानक की एक अलग व्याख्या का पालन किया, जो void_tअपेक्षा के अनुरूप काम नहीं करता था। यक ने पहले से ही काम उपलब्ध कराया है: निम्नलिखित की परिभाषा का उपयोग करें void_t( पैरामीटर सूची कार्यों में void_t लेकिन वापसी प्रकार के रूप में नहीं ):

#if __GNUC__ < 5 && ! defined __clang__
// https://stackoverflow.com/a/28967049/1353549
template <typename...>
struct voider
{
  using type = void;
};
template <typename...Ts>
using void_t = typename voider<Ts...>::type;
#else
template <typename...>
using void_t = void;
#endif

क्या गैर-सदस्य कार्यों का पता लगाने के लिए इसका विस्तार करना संभव है?
प्लाज़्मासेल

हाँ यकीनन। उदाहरणों को ध्यान से देखें: आप मूल रूप से एक अभिव्यक्ति प्रदान करते हैं और जांचें कि क्या यह वैध है। कुछ भी नहीं इस अभिव्यक्ति के लिए केवल एक सदस्य फ़ंक्शन कॉल की आवश्यकता है।
एकिम डेम

N4502 ( open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf ) भविष्य का रास्ता है ... मैं प्रकारों के बारे में चीजों का पता लगाने का एक अच्छा तरीका तलाश रहा था और N4502 तरीका है जाना।
त्लोनुक

11

यह सामान्य समस्या के लिए C ++ 11 समाधान है यदि "अगर मैंने एक्स किया, तो क्या यह संकलन करेगा?"

template<class> struct type_sink { typedef void type; }; // consumes a type, and makes it `void`
template<class T> using type_sink_t = typename type_sink<T>::type;
template<class T, class=void> struct has_to_string : std::false_type {}; \
template<class T> struct has_to_string<
  T,
  type_sink_t< decltype( std::declval<T>().toString() ) >
>: std::true_type {};

विशेषता has_to_stringऐसी है कि has_to_string<T>::valueहै trueयदि और केवल यदि Tएक विधि है .toStringकि इस संदर्भ में 0 तर्कों द्वारा सक्रिय किया जा सकता है।

अगला, मैं टैग प्रेषण का उपयोग करूंगा:

namespace details {
  template<class T>
  std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) {
    return obj->toString();
  }
  template<class T>
  std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) {
    return "toString not defined";
  }
}
template<class T>
std::string optionalToString(T* obj) {
  return details::optionalToString_helper( obj, has_to_string<T>{} );
}

जो जटिल SFINAE अभिव्यक्तियों की तुलना में अधिक बनाए रखने के लिए जाता है।

आप इन लक्षणों को एक मैक्रो के साथ लिख सकते हैं यदि आप अपने आप को यह करते हुए पाते हैं, लेकिन वे अपेक्षाकृत सरल हैं (प्रत्येक कुछ लाइनें) तो शायद इसके लायक नहीं हैं:

#define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \
template<class T, class=void> struct TRAIT_NAME : std::false_type {}; \
template<class T> struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {};

उपरोक्त क्या एक मैक्रो बनाता है MAKE_CODE_TRAIT। आप इसे उस विशेषता का नाम देते हैं जिसे आप चाहते हैं, और कुछ कोड जो प्रकार का परीक्षण कर सकते हैं T। इस प्रकार:

MAKE_CODE_TRAIT( has_to_string, std::declval<T>().toString() )

उपरोक्त लक्षण वर्ग बनाता है।

एक तरफ के रूप में, उपरोक्त तकनीक एमएस "अभिव्यक्ति SFINAE" का हिस्सा है, और उनके 2013 संकलक बहुत मुश्किल विफल हो जाते हैं।

ध्यान दें कि C ++ 1y में निम्नलिखित सिंटैक्स संभव है:

template<class T>
std::string optionalToString(T* obj) {
  return compiled_if< has_to_string >(*obj, [&](auto&& obj) {
    return obj.toString();
  }) *compiled_else ([&]{ 
    return "toString not defined";
  });
}

जो इनलाइन संकलन की सशर्त शाखा है जो C ++ सुविधाओं के बहुत से दुरुपयोग करता है। ऐसा करना संभवत: इसके लायक नहीं है, क्योंकि लाभ (कोड इनलाइन होने का) लागत के लायक नहीं है (यह कैसे काम करता है यह समझने के लिए किसी के बगल में), लेकिन उस उपरोक्त समाधान का अस्तित्व रुचि का हो सकता है।


क्या यह निजी मामलों को संभालता है?
टॉवर १२

@ Tower120 मुझे प्रयोग करना होगा: कैसे टेम्पलेट्स निजी / सार्वजनिक / संरक्षित के साथ बातचीत करते हैं मेरे लिए थोड़ा अस्पष्ट है। इससे कोई फर्क नहीं पड़ेगा कि आप कहां पर आमंत्रित करते हैं has_to_string
यक्क - एडम नेवरामॉन्ट

लेकिन आप जानते हैं, अगर दूसरी तरफ से देखें ... हम व्युत्पन्न वर्ग से संरक्षित सदस्यों तक पहुंच सकते हैं। हो सकता है कि अगर यह सारा सामान इनसाइड क्लास में डाल दिया जाए, और इसे स्ट्रक्चर से कॉन्स्टैक्स के फंक्शन्स में बदल दिया जाए ...
टावर १२

यहाँ, इस coliru.stacked-croched.com/a/ee94d16e7c07e093 को देखें, मैं इसे अभी नहीं बना सकता
टॉवर १२

@ Tower120 C ++ 1y यह काम करता है: coliru.stacked-croched.com/a/d8cdfff24a171394
- एडम नेवरामोंट

10

यहाँ कुछ उपयोग स्निपेट दिए गए हैं: * इस सब के लिए हिम्मत कम है

xकिसी दिए गए वर्ग में सदस्य के लिए जाँच करें । Var, func, class, Union या enum हो सकता है:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

सदस्य समारोह के लिए जाँच करें void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

सदस्य चर के लिए जाँच करें x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

सदस्य वर्ग के लिए जाँच करें x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

सदस्य संघ के लिए जाँच करें x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

सदस्य enum के लिए जाँच करें x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

xहस्ताक्षर की परवाह किए बिना किसी भी सदस्य समारोह के लिए जाँच करें :

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

या

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

विवरण और कोर:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

मैक्रोज़ (एल डियाब्लो!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

1
क्या आपके पास कोई विचार है कि अगर हम sig_check<func_sig, &T::func_name>नि: शुल्क फ़ंक्शन चेकिंग में बदल जाते हैं: sig_check<func_sig, &func_name>यह "अघोषित पहचानकर्ता" के साथ निर्माण करने में विफल रहता है, तो उस फ़ंक्शन के नाम का उल्लेख करना जिसे हम जांचना चाहते हैं? क्योंकि मैं SFINAE से यह अपेक्षा करूंगा कि यह एक त्रुटि नहीं है, यह सिर्फ सदस्यों के लिए है, मुफ्त कार्यों के लिए क्यों नहीं?
v.oddou

मुझे लगता है कि यह इस तथ्य के साथ कुछ करना होगा कि एक नि: शुल्क फ़ंक्शन एक वर्ग या संरचना नहीं है। सी ++ में कई विरासत तंत्र पर एक सदस्य की उपस्थिति को वास्तव में केंद्र में लाने की यह तकनीक एक ठूंठ वर्ग के बीच अस्पष्टता को मजबूर करती है जो केवल उस सदस्य की मेजबानी करने के उद्देश्य से मौजूद है जिसे आप उस वर्ग के लिए जाँच रहे हैं जो वास्तव में सदस्य के लिए जाँच कर रहा है। में, यह एक दिलचस्प सवाल है, हालांकि, इसके बारे में नहीं सोचा था। आप अन्य C ++ 11/14 सदस्य-जांच तकनीकों के लिए चारों ओर देख सकते हैं, मैंने नए मानक में कुछ चतुर चीजें देखी हैं।
ब्रेट रॉसिएर

आपके उत्तर के लिए धन्यवाद, मुझे लगता है कि मुझे अधिक गहराई से जांचना पड़ सकता है कि इंटेल आपको विरासत के बारे में देता है, क्योंकि अब तक मैंने एसएफआईएनएई पर भरोसा करने के बीच किसी भी संबंध को नहीं देखा था एक अभिव्यक्ति बनाने के लिए जो सही अभिव्यक्ति व्यक्त नहीं करेगा। एक टेम्पलेट प्रकार पैरामीटर में एक सदस्य, और कई विरासत। लेकिन मैं पूरी तरह से मानता हूं कि सी ++ में भी दूर की अवधारणाएं एक-दूसरे पर खून बहा सकती हैं। अब मुफ्त कार्यों के लिए यह प्रश्न दिलचस्प है: stackoverflow.com/questions/26744589 टीसी उत्तर "अघोषित पहचानकर्ता" से बचने के लिए डमी घोषित करने की एक चाल का उपयोग करता प्रतीत होता है
v.oddou

8

मैंने इसका जवाब एक और सूत्र में लिखा है कि (उपरोक्त समाधानों के विपरीत) विरासत में मिले सदस्य कार्यों की भी जाँच करता है:

वंशानुगत सदस्य कार्यों के लिए जाँच करने के लिए SFINAE

उस समाधान से कुछ उदाहरण इस प्रकार हैं:

उदाहरण 1:

हम निम्नलिखित हस्ताक्षर के साथ एक सदस्य के लिए जाँच कर रहे हैं: T::const_iterator begin() const

template<class T> struct has_const_begin
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U> 
    static Yes test(U const * data, 
                    typename std::enable_if<std::is_same<
                             typename U::const_iterator, 
                             decltype(data->begin())
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};

कृपया ध्यान दें कि यह विधि की स्थिरता की भी जांच करता है, और आदिम प्रकारों के साथ भी काम करता है। (मेरा मतलब है has_const_begin<int>::valueकि गलत है और एक संकलन-समय त्रुटि का कारण नहीं है।)

उदाहरण 2

अब हम हस्ताक्षर की तलाश कर रहे हैं: void foo(MyClass&, unsigned)

template<class T> struct has_foo
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U>
    static Yes test(U * data, MyClass* arg1 = 0,
                    typename std::enable_if<std::is_void<
                             decltype(data->foo(*arg1, 1u))
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};

कृपया ध्यान दें कि MyClass को डिफ़ॉल्ट रूप से रचनात्मक होने या किसी विशेष अवधारणा को पूरा करने की आवश्यकता नहीं है। तकनीक टेम्पलेट सदस्यों के साथ-साथ काम करती है।

मैं इस बारे में बेसब्री से इंतजार कर रहा हूं।


7

अब यह एक अच्छी छोटी पहेली थी - बड़ा सवाल!

यहां निकोला बोनेली के समाधान का एक विकल्प है जो गैर-मानक typeofऑपरेटर पर भरोसा नहीं करता है ।

दुर्भाग्य से, यह GCC (MinGW) 3.4.5 या डिजिटल मंगल 8.42n पर काम नहीं करता है, लेकिन यह MSVC के सभी संस्करणों (VC6 सहित) और Comeau C ++ पर काम करता है।

लंबी टिप्पणी ब्लॉक में यह विवरण है कि यह कैसे काम करता है (या काम करने वाला है)। जैसा कि यह कहा गया है, मुझे यकीन नहीं है कि कौन सा व्यवहार मानकों के अनुरूप है - मैं उस पर टिप्पणी का स्वागत करूंगा।


अपडेट - 7 नवंबर २०० Nov:

ऐसा लगता है कि जबकि यह कोड वाक्य-रचना सही है, MSVC और Comeau C ++ शो का व्यवहार मानक का पालन नहीं करता है ( लियोन टिम्मरमन्स के लिए धन्यवाद और मुझे सही दिशा में इंगित करने के लिए जलाया जाता है)। C ++ 03 मानक निम्नलिखित कहता है:

14.6.2 आश्रित नाम [temp.dep]

अनुच्छेद ३

क्लास टेम्प्लेट या क्लास टेम्प्लेट के एक सदस्य की परिभाषा में, यदि क्लास टेम्प्लेट का एक बेस क्लास टेम्प्लेट-पैरामीटर पर निर्भर करता है, तो बेस क्लास स्कोप को अयोग्य नाम के लुकअप के दौरान क्लास की परिभाषा के बिंदु पर जांचा नहीं जाता है। टेम्पलेट या सदस्य या वर्ग टेम्पलेट या सदस्य की तात्कालिकता के दौरान।

इसलिए, ऐसा लगता है कि जब MSVC या कॉम्पे , टेम्पलेट को तत्काल करने पर कॉल साइट पर नाम लुकअप करने के toString()सदस्य फ़ंक्शन पर विचार करते हैं , तो यह गलत है (भले ही यह वास्तव में वह व्यवहार है जो मैं इस मामले में देख रहा था)।TdoToString()

जीसीसी और डिजिटल मंगल का व्यवहार सही प्रतीत होता है - दोनों मामलों में गैर-सदस्यीय toString()फ़ंक्शन कॉल के लिए बाध्य है।

चूहों - मैंने सोचा कि मुझे एक चतुर समाधान मिल सकता है, इसके बजाय मैंने एक जोड़ी संकलक कीड़े को उजागर किया ...


#include <iostream>
#include <string>

struct Hello
{
    std::string toString() {
        return "Hello";
    }
};

struct Generic {};


// the following namespace keeps the toString() method out of
//  most everything - except the other stuff in this
//  compilation unit

namespace {
    std::string toString()
    {
        return "toString not defined";
    }

    template <typename T>
    class optionalToStringImpl : public T
    {
    public:
        std::string doToString() {

            // in theory, the name lookup for this call to 
            //  toString() should find the toString() in 
            //  the base class T if one exists, but if one 
            //  doesn't exist in the base class, it'll 
            //  find the free toString() function in 
            //  the private namespace.
            //
            // This theory works for MSVC (all versions
            //  from VC6 to VC9) and Comeau C++, but
            //  does not work with MinGW 3.4.5 or 
            //  Digital Mars 8.42n
            //
            // I'm honestly not sure what the standard says 
            //  is the correct behavior here - it's sort 
            //  of like ADL (Argument Dependent Lookup - 
            //  also known as Koenig Lookup) but without
            //  arguments (except the implied "this" pointer)

            return toString();
        }
    };
}

template <typename T>
std::string optionalToString(T & obj)
{
    // ugly, hacky cast...
    optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);

    return temp->doToString();
}



int
main(int argc, char *argv[])
{
    Hello helloObj;
    Generic genericObj;

    std::cout << optionalToString( helloObj) << std::endl;
    std::cout << optionalToString( genericObj) << std::endl;
    return 0;
}

1
नहीं, यह मानकों के अनुरूप नहीं है, हालांकि मुझे लगता है कि यह जीसीसी में काम करेगा यदि आप -fpermissive विकल्प को चालू करते हैं।
लियोन टिम्मरमैन

मुझे पता है कि टिप्पणियां बहुत जगह नहीं देती हैं, लेकिन क्या आप इस बात की जानकारी दे सकते हैं कि यह मानकों के अनुरूप क्यों नहीं है? (मैं बहस नहीं कर रहा हूं - मैं उत्सुक हूं)
माइकल बूर

माइक बी: मानक 3.10 p15 में कहता है: "यदि कोई प्रोग्राम किसी वस्तु के संचित मूल्य तक पहुँचने का प्रयास करता है, तो निम्न में से किसी एक प्रकार के व्यवहार के अलावा व्यवहार अपरिभाषित है" और उस सूची में वास्तव में वह मामला शामिल नहीं है जिसमें आप शामिल हैं करना।
जोहान्स शाउब -

4
मुझे यकीन नहीं है कि यह मेरे बारे में एक और टिप्पणी क्यों नहीं जोड़ रहा है: आपकी कॉलिंग स्ट्रिंग अयोग्य है। इसलिए यह हमेशा फ्री फ़ंक्शन को कॉल करेगा और आधार में कभी भी नहीं होगा, क्योंकि बेसकैश टेम्पलेट प्रकार के पैरामीटर पर निर्भर है।
जोहान्स शाउब -

@litb: संकेत के लिए धन्यवाद। मुझे नहीं लगता कि 3.10 यहां लागू होता है। DoToString () के अंदर toString () कॉल "एक लैवल्यू के माध्यम से किसी ऑब्जेक्ट के संग्रहीत मूल्य तक पहुंच नहीं है"। लेकिन आपकी दूसरी टिप्पणी सही है। मैं जवाब अपडेट कर दूंगा।
माइकल बूर

6

लीट द्वारा यहां प्रस्तुत मानक C ++ समाधान अपेक्षित रूप से काम नहीं करेगा यदि विधि एक बेस क्लास में परिभाषित की जाती है।

इस स्थिति को संभालने वाले समाधान के लिए देखें:

रूसी में: http://www.rsdn.ru/forum/message/2759773.1.aspx

Roman.Perepelitsa द्वारा अंग्रेजी अनुवाद: http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/bgia7b4c645e449f?pli=1

यह पागलपनपूर्ण है। हालाँकि इस विलेय के साथ एक मुद्दा यह है कि संकलक त्रुटियां देता है यदि परीक्षण किया जा रहा प्रकार एक है जिसे आधार वर्ग (जैसे आदिम प्रकार) के रूप में इस्तेमाल नहीं किया जा सकता है

विज़ुअल स्टूडियो में, मैंने देखा कि यदि कोई तर्क नहीं है, तो विधि के साथ काम करते हुए आकार में अभिव्यक्ति में कटौती करने के लिए निरर्थक () को अतिरिक्त जोड़ी जाने की आवश्यकता है।


हम्म, उस पोस्ट विचारों का उपयोग करके अपना खुद का संस्करण विकसित किया है, मुझे पता चला कि विचार में कुछ अन्य कमियां हैं, इसलिए मैंने फिर से अपने उत्तर से कोड हटा दिया। एक यह है कि सभी कार्यों को लक्ष्य प्रकार में सार्वजनिक होना चाहिए। इसलिए आप इसमें "f" फ़ंक्शन की जांच नहीं कर सकते हैं: struct g { void f(); private: void f(int); };क्योंकि फ़ंक्शन में से एक निजी है (यह इसलिए है क्योंकि कोड करता है using g::f;, जो किसी भी fपहुंच योग्य नहीं होने पर इसे विफल कर देता है )।
जोहान्स स्काउब -

6

MSVC में __if_exists और __if_not_exists कीवर्ड ( Doc ) हैं। निकोला के टाइपो-एसएफआईएनएई दृष्टिकोण के साथ मिलकर मैं जीसीसी और एमएसवीसी के लिए एक चेक बना सकता था जैसे ओपी ने देखा था।

अद्यतन: स्रोत यहां पाया जा सकता है


6

एक Has_fooअवधारणा जांच लिखकर, SFINAE और टेम्पलेट आंशिक विशेषज्ञता का उपयोग करने वाला एक उदाहरण :

#include <type_traits>
struct A{};

struct B{ int foo(int a, int b);};

struct C{void foo(int a, int b);};

struct D{int foo();};

struct E: public B{};

// available in C++17 onwards as part of <type_traits>
template<typename...>
using void_t = void;

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

template<typename T> 
struct Has_foo<T, void_t<
    std::enable_if_t<
        std::is_same<
            int, 
            decltype(std::declval<T>().foo((int)0, (int)0))
        >::value
    >
>>: std::true_type{};


static_assert(not Has_foo<A>::value, "A does not have a foo");
static_assert(Has_foo<B>::value, "B has a foo");
static_assert(not Has_foo<C>::value, "C has a foo with the wrong return. ");
static_assert(not Has_foo<D>::value, "D has a foo with the wrong arguments. ");
static_assert(Has_foo<E>::value, "E has a foo since it inherits from B");

5

मैंने https://stackoverflow.com/a/264088/2712152 में दिए गए समाधान को संशोधित किया इसे थोड़ा और सामान्य बनाने के लिए । इसके अलावा चूंकि यह किसी भी नए C ++ 11 फीचर्स का उपयोग नहीं करता है इसलिए हम इसे पुराने कंपाइलरों के साथ उपयोग कर सकते हैं और इसे msvc के साथ भी काम करना चाहिए। लेकिन कंपाइलरों को C99 को इसका उपयोग करने में सक्षम करना चाहिए क्योंकि यह वैरिएड मैक्रोज़ का उपयोग करता है।

निम्न मैक्रो का उपयोग यह जांचने के लिए किया जा सकता है कि क्या किसी विशेष वर्ग के पास एक विशेष टाइपफ़ाइफ़ है या नहीं।

/** 
 * @class      : HAS_TYPEDEF
 * @brief      : This macro will be used to check if a class has a particular
 * typedef or not.
 * @param typedef_name : Name of Typedef
 * @param name  : Name of struct which is going to be run the test for
 * the given particular typedef specified in typedef_name
 */
#define HAS_TYPEDEF(typedef_name, name)                           \
   template <typename T>                                          \
   struct name {                                                  \
      typedef char yes[1];                                        \
      typedef char no[2];                                         \
      template <typename U>                                       \
      struct type_check;                                          \
      template <typename _1>                                      \
      static yes& chk(type_check<typename _1::typedef_name>*);    \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

निम्न मैक्रो का उपयोग यह जांचने के लिए किया जा सकता है कि किसी विशेष वर्ग के पास किसी विशेष सदस्य फ़ंक्शन है या किसी भी दिए गए तर्कों के साथ नहीं।

/** 
 * @class      : HAS_MEM_FUNC
 * @brief      : This macro will be used to check if a class has a particular
 * member function implemented in the public section or not. 
 * @param func : Name of Member Function
 * @param name : Name of struct which is going to be run the test for
 * the given particular member function name specified in func
 * @param return_type: Return type of the member function
 * @param ellipsis(...) : Since this is macro should provide test case for every
 * possible member function we use variadic macros to cover all possibilities
 */
#define HAS_MEM_FUNC(func, name, return_type, ...)                \
   template <typename T>                                          \
   struct name {                                                  \
      typedef return_type (T::*Sign)(__VA_ARGS__);                \
      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::func>*);              \
      template <typename>                                         \
      static no& chk(...);                                        \
      static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
   }

हम has_typedef और has_mem_func के लिए जाँच करने के लिए उपरोक्त 2 मैक्रो का उपयोग कर सकते हैं:

class A {
public:
  typedef int check;
  void check_function() {}
};

class B {
public:
  void hello(int a, double b) {}
  void hello() {}
};

HAS_MEM_FUNC(check_function, has_check_function, void, void);
HAS_MEM_FUNC(hello, hello_check, void, int, double);
HAS_MEM_FUNC(hello, hello_void_check, void, void);
HAS_TYPEDEF(check, has_typedef_check);

int main() {
  std::cout << "Check Function A:" << has_check_function<A>::value << std::endl;
  std::cout << "Check Function B:" << has_check_function<B>::value << std::endl;
  std::cout << "Hello Function A:" << hello_check<A>::value << std::endl;
  std::cout << "Hello Function B:" << hello_check<B>::value << std::endl;
  std::cout << "Hello void Function A:" << hello_void_check<A>::value << std::endl;
  std::cout << "Hello void Function B:" << hello_void_check<B>::value << std::endl;
  std::cout << "Check Typedef A:" << has_typedef_check<A>::value << std::endl;
  std::cout << "Check Typedef B:" << has_typedef_check<B>::value << std::endl;
}

आप टेम्पलेट तर्कों के साथ सदस्य कार्यों का समर्थन करने के लिए इसमें सुधार कर सकते हैं। टेम्प्लेट <टाइपनेम टी> को टेम्प्लेट में टाइप करें <टाइपनेम टी, टाइपनेम ... आर्ग्स>, फिर आप वैरिएबल टेम्प्लेट आर्ग्स के साथ एक चेक स्ट्रक्चर बनाने के लिए अपने मैक्रो एल्पिस में "आर्ग्स ..." का उपयोग कर सकते हैं। जैसे। "शून्य onNext (const T &)" विधि का पता लगाएं HAS_MEM_FUNC( onNext, has_memberfn_onNext, void, Args... ); ...template <typename V> struct Foo { void onNext(const V &); static_assert< has_memberfn_onNext<Foo<V>,const V &>::value, "API fail" ); };
ACIClic

4

अजीब बात यह है कि इस बहुत साइट पर एक बार मैंने देखा कि निम्नलिखित अच्छी चाल का सुझाव नहीं दिया गया है:

template <class T>
struct has_foo
{
    struct S { void foo(...); };
    struct derived : S, T {};

    template <typename V, V> struct W {};

    template <typename X>
    char (&test(W<void (X::*)(), &X::foo> *))[1];

    template <typename>
    char (&test(...))[2];

    static const bool value = sizeof(test<derived>(0)) == 1;
};

आपको यह सुनिश्चित करना होगा कि टी एक वर्ग है। ऐसा लगता है कि फू की तलाश में अस्पष्टता एक प्रतिस्थापन विफलता है। मैंने इसे gcc पर काम किया है, यह सुनिश्चित नहीं है कि क्या यह मानक है।


3

जेनेरिक टेम्प्लेट जो जाँचने के लिए इस्तेमाल किया जा सकता है कि क्या कुछ "फीचर" प्रकार द्वारा समर्थित है:

#include <type_traits>

template <template <typename> class TypeChecker, typename Type>
struct is_supported
{
    // these structs are used to recognize which version
    // of the two functions was chosen during overload resolution
    struct supported {};
    struct not_supported {};

    // this overload of chk will be ignored by SFINAE principle
    // if TypeChecker<Type_> is invalid type
    template <typename Type_>
    static supported chk(typename std::decay<TypeChecker<Type_>>::type *);

    // ellipsis has the lowest conversion rank, so this overload will be
    // chosen during overload resolution only if the template overload above is ignored
    template <typename Type_>
    static not_supported chk(...);

    // if the template overload of chk is chosen during
    // overload resolution then the feature is supported
    // if the ellipses overload is chosen the the feature is not supported
    static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value;
};

वह टेम्प्लेट जो यह जाँचता है कि क्या कोई ऐसी विधि है fooजो हस्ताक्षर के अनुकूल हैdouble(const char*)

// if T doesn't have foo method with the signature that allows to compile the bellow
// expression then instantiating this template is Substitution Failure (SF)
// which Is Not An Error (INAE) if this happens during overload resolution
template <typename T>
using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>())));

उदाहरण

// types that support has_foo
struct struct1 { double foo(const char*); };            // exact signature match
struct struct2 { int    foo(const std::string &str); }; // compatible signature
struct struct3 { float  foo(...); };                    // compatible ellipsis signature
struct struct4 { template <typename T>
                 int    foo(T t); };                    // compatible template signature

// types that do not support has_foo
struct struct5 { void        foo(const char*); }; // returns void
struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double
struct struct7 { double      foo(      int *); }; // const char* can't be converted to int*
struct struct8 { double      bar(const char*); }; // there is no foo method

int main()
{
    std::cout << std::boolalpha;

    std::cout << is_supported<has_foo, int    >::value << std::endl; // false
    std::cout << is_supported<has_foo, double >::value << std::endl; // false

    std::cout << is_supported<has_foo, struct1>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct2>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct3>::value << std::endl; // true
    std::cout << is_supported<has_foo, struct4>::value << std::endl; // true

    std::cout << is_supported<has_foo, struct5>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct6>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct7>::value << std::endl; // false
    std::cout << is_supported<has_foo, struct8>::value << std::endl; // false

    return 0;
}

http://coliru.stacked-crooked.com/a/83c6a631ed42cea4


क्या has_fooटेम्पलेट कॉल में इनलाइन करने का कोई तरीका है is_supported। मैं क्या चाहूंगा जैसे कुछ कहते हैं std::cout << is_supported<magic.foo(), struct1>::value << std::endl;:। इसका कारण, मैं has_fooप्रत्येक अलग-अलग फ़ंक्शन हस्ताक्षर के लिए परिभाषित करना चाहता हूं जिसे मैं फ़ंक्शन की जांच करने से पहले जांचना चाहता हूं?
CJCombrink

2

इस समाधान के बारे में कैसे?

#include <type_traits>

template <typename U, typename = void> struct hasToString : std::false_type { };

template <typename U>
struct hasToString<U,
  typename std::enable_if<bool(sizeof(&U::toString))>::type
> : std::true_type { };

toStringअतिभारित होने पर विफल रहता है, जैसा &U::toStringकि अस्पष्ट है।
याक्क - एडम नेवरामॉन्ट

@ यक मुझे लगता है कि एक कलाकार इस समस्या को ठीक कर सकता है।
user1095108

2

यहां बहुत सारे उत्तर हैं, लेकिन मैं एक संस्करण को खोजने में विफल रहा, जो कि वास्तविक विधि रिज़ॉल्यूशन ऑर्डर करता है, जबकि किसी भी नए सी ++ सुविधाओं का उपयोग नहीं करता है (केवल सी ++ 98 सुविधाओं का उपयोग करके)।
नोट: यह संस्करण vc ++ 2013, g ++ 5.2.0 और ऑनलाइन कंपाइलर के साथ परीक्षण और काम कर रहा है।

इसलिए मैं एक संस्करण के साथ आया, जो केवल आकार का उपयोग करता है ():

template<typename T> T declval(void);

struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);

struct yes { char v[1]; };
struct no  { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};

template<typename T>
struct has_awesome_member {
 template<typename U> static yes_no<(sizeof((
   declval<U>().awesome_member(),fake_void()
  ))!=0)> check(int);
 template<typename> static no check(...);
 enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};


struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };

static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");

लाइव डेमो (विस्तारित वापसी प्रकार की जाँच और vc ++ 2010 वर्कअराउंड के साथ): http://cpp.sh/5b2vs

कोई स्रोत नहीं, जैसा कि मैं खुद इसके साथ आया था।

जी ++ कंपाइलर पर लाइव डेमो चलाते समय, कृपया ध्यान दें कि सरणी आकार 0 की अनुमति है, जिसका अर्थ है कि इस्तेमाल किया जाने वाला static_assert कंपाइलर त्रुटि को ट्रिगर नहीं करेगा, भले ही वह विफल हो।
आम तौर पर इस्तेमाल किया जाने वाला काम-मैक्रो में 'टाइपफ़ेड' को 'बाहरी' के साथ बदलना है।


नहीं, लेकिन मैं इसे स्वयं घोषित कर रहा हूं और यह rvalue (मेरे कोड के शीर्ष पर देखो) का उपयोग नहीं करता है। या आप बस खुद को समझा सकते हैं और लाइव डेमो को c ++ 98 मोड में आज़मा सकते हैं। PS: static_assert c ++ 98 या तो नहीं है, लेकिन वर्क-अराउंड (लाइव डेमो) हैं
user3296587

डी 'ओह! चूक गए। :-)
इयान नी-लुईस

आपके स्थिर दावे काम नहीं करते हैं। आपको 0 के बजाय सरणी आकार -1 का उपयोग करने की आवश्यकता है (डालने का प्रयास करें static_assert(false);)। मैं इसका उपयोग CRTP के संबंध में कर रहा था, जहां मैं यह निर्धारित करना चाहता हूं कि क्या व्युत्पन्न वर्ग का कोई विशेष कार्य है - जो काम नहीं करता है, फिर भी आपका जोर हमेशा पास रहता है। मैंने कुछ बाल खो दिए।
सूअर

मैं मान रहा हूँ कि आप g ++ का उपयोग कर रहे हैं। कृपया ध्यान दें, कि gcc / g ++ में एक एक्सटेंशन है जो शून्य-आकार की सरणी ( gcc.gnu.org/oniltocs/gcc/Zero-Length.html ) के लिए अनुमति देता है
user3296587

आप संभवतः इसे फिर से लिखना कर सकते हैं ताकि ऑपरेटर को अधिभार न डालें,? उदाहरण के लिए एक और ऑपरेटर चुनें? इसके अलावा, has_awesome_member के अलावा कुछ भी नाम के प्रदूषण से बचें?
einpoklum

1

यहाँ मेरा संस्करण है जो संभावित सदस्य फ़ंक्शन को ओवरलोड करता है, मनमाने ढंग से समता के साथ, टेम्पलेट सदस्य कार्यों सहित, संभवतः डिफ़ॉल्ट तर्कों के साथ। यह दिए गए arg प्रकारों के साथ कुछ वर्ग प्रकार के लिए एक सदस्य फ़ंक्शन कॉल करते समय 3 परस्पर अनन्य परिदृश्यों को अलग करता है: (1) वैध, या (2) अस्पष्ट, या (3) गैर-व्यवहार्य। उदाहरण उपयोग:

#include <string>
#include <vector>

HAS_MEM(bar)
HAS_MEM_FUN_CALL(bar)

struct test
{
   void bar(int);
   void bar(double);
   void bar(int,double);

   template < typename T >
   typename std::enable_if< not std::is_integral<T>::value >::type
   bar(const T&, int=0){}

   template < typename T >
   typename std::enable_if< std::is_integral<T>::value >::type
   bar(const std::vector<T>&, T*){}

   template < typename T >
   int bar(const std::string&, int){}
};

अब आप इसे इस तरह से उपयोग कर सकते हैं:

int main(int argc, const char * argv[])
{
   static_assert( has_mem_bar<test>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(char const*,long)>::value , "");
   static_assert( has_valid_mem_fun_call_bar<test(std::string&,long)>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(std::vector<int>, int*)>::value , "");
   static_assert( has_no_viable_mem_fun_call_bar<test(std::vector<double>, double*)>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(int)>::value , "");
   static_assert( std::is_same<void,result_of_mem_fun_call_bar<test(int)>::type>::value , "");

   static_assert( has_valid_mem_fun_call_bar<test(int,double)>::value , "");
   static_assert( not has_valid_mem_fun_call_bar<test(int,double,int)>::value , "");

   static_assert( not has_ambiguous_mem_fun_call_bar<test(double)>::value , "");
   static_assert( has_ambiguous_mem_fun_call_bar<test(unsigned)>::value , "");

   static_assert( has_viable_mem_fun_call_bar<test(unsigned)>::value , "");
   static_assert( has_viable_mem_fun_call_bar<test(int)>::value , "");

   static_assert( has_no_viable_mem_fun_call_bar<test(void)>::value , "");

   return 0;
}

यहाँ कोड दिया गया है, जिसे c ++ 11 में लिखा गया है, हालाँकि, आप इसे आसानी से (मामूली tweaks के साथ) गैर-c ++ 11 में टाइप कर सकते हैं जिसमें टाइपो एक्सटेंशन (जैसे gcc) है। आप HAS_MEM मैक्रो को अपने से बदल सकते हैं।

#pragma once

#if __cplusplus >= 201103

#include <utility>
#include <type_traits>

#define HAS_MEM(mem)                                                                                     \
                                                                                                     \
template < typename T >                                                                               \
struct has_mem_##mem                                                                                  \
{                                                                                                     \
  struct yes {};                                                                                     \
  struct no  {};                                                                                     \
                                                                                                     \
  struct ambiguate_seed { char mem; };                                                               \
  template < typename U > struct ambiguate : U, ambiguate_seed {};                                   \
                                                                                                     \
  template < typename U, typename = decltype(&U::mem) > static constexpr no  test(int);              \
  template < typename                                 > static constexpr yes test(...);              \
                                                                                                     \
  static bool constexpr value = std::is_same<decltype(test< ambiguate<T> >(0)),yes>::value ;         \
  typedef std::integral_constant<bool,value>    type;                                                \
};


#define HAS_MEM_FUN_CALL(memfun)                                                                         \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_valid_mem_fun_call_##memfun;                                                               \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_valid_mem_fun_call_##memfun< T(Args...) >                                                  \
{                                                                                                     \
  struct yes {};                                                                                     \
  struct no  {};                                                                                     \
                                                                                                     \
  template < typename U, bool = has_mem_##memfun<U>::value >                                         \
  struct impl                                                                                        \
  {                                                                                                  \
     template < typename V, typename = decltype(std::declval<V>().memfun(std::declval<Args>()...)) > \
     struct test_result { using type = yes; };                                                       \
                                                                                                     \
     template < typename V > static constexpr typename test_result<V>::type test(int);               \
     template < typename   > static constexpr                            no test(...);               \
                                                                                                     \
     static constexpr bool value = std::is_same<decltype(test<U>(0)),yes>::value;                    \
     using type = std::integral_constant<bool, value>;                                               \
  };                                                                                                 \
                                                                                                     \
  template < typename U >                                                                            \
  struct impl<U,false> : std::false_type {};                                                         \
                                                                                                     \
  static constexpr bool value = impl<T>::value;                                                      \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_ambiguous_mem_fun_call_##memfun;                                                           \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_ambiguous_mem_fun_call_##memfun< T(Args...) >                                              \
{                                                                                                     \
  struct ambiguate_seed { void memfun(...); };                                                       \
                                                                                                     \
  template < class U, bool = has_mem_##memfun<U>::value >                                            \
  struct ambiguate : U, ambiguate_seed                                                               \
  {                                                                                                  \
    using ambiguate_seed::memfun;                                                                    \
    using U::memfun;                                                                                 \
  };                                                                                                 \
                                                                                                     \
  template < class U >                                                                               \
  struct ambiguate<U,false> : ambiguate_seed {};                                                     \
                                                                                                     \
  static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate<T>(Args...) >::value; \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_viable_mem_fun_call_##memfun;                                                              \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_viable_mem_fun_call_##memfun< T(Args...) >                                                 \
{                                                                                                     \
  static constexpr bool value = has_valid_mem_fun_call_##memfun<T(Args...)>::value                   \
                             or has_ambiguous_mem_fun_call_##memfun<T(Args...)>::value;              \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct has_no_viable_mem_fun_call_##memfun;                                                           \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct has_no_viable_mem_fun_call_##memfun < T(Args...) >                                             \
{                                                                                                     \
  static constexpr bool value = not has_viable_mem_fun_call_##memfun<T(Args...)>::value;             \
  using type = std::integral_constant<bool, value>;                                                  \
};                                                                                                    \
                                                                                                     \
template < typename Signature >                                                                       \
struct result_of_mem_fun_call_##memfun;                                                               \
                                                                                                     \
template < typename T, typename... Args >                                                             \
struct result_of_mem_fun_call_##memfun< T(Args...) >                                                  \
{                                                                                                     \
  using type = decltype(std::declval<T>().memfun(std::declval<Args>()...));                          \
};

#endif


1

आप C ++ 14 में सभी मेटाप्रोग्रामिंग को छोड़ सकते हैं, और fit::conditionalइसे फ़िट लाइब्रेरी से उपयोग करके लिख सकते हैं :

template<class T>
std::string optionalToString(T* x)
{
    return fit::conditional(
        [](auto* obj) -> decltype(obj->toString()) { return obj->toString(); },
        [](auto*) { return "toString not defined"; }
    )(x);
}

आप कार्य को सीधे लंबोदर से भी बना सकते हैं:

FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional(
    [](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); },
    [](auto*) -> std::string { return "toString not defined"; }
);

हालाँकि, यदि आप एक कंपाइलर का उपयोग कर रहे हैं जो जेनेरिक लैम्ब्डा का समर्थन नहीं करता है, तो आपको अलग-अलग फ़ंक्शन ऑब्जेक्ट लिखना होगा:

struct withToString
{
    template<class T>
    auto operator()(T* obj) const -> decltype(obj->toString(), std::string())
    {
        return obj->toString();
    }
};

struct withoutToString
{
    template<class T>
    std::string operator()(T*) const
    {
        return "toString not defined";
    }
};

FIT_STATIC_FUNCTION(optionalToString) = fit::conditional(
    withToString(),
    withoutToString()
);

1
यह लिखना कितना आसान है ताकि fitमानक के अलावा किसी अन्य पुस्तकालय पर निर्भर न रहना पड़े ?
einpoklum

1

C ++ 20 के साथ आप निम्नलिखित लिख सकते हैं:

template<typename T>
concept has_toString = requires(const T& t) {
    t.toString();
};

template<typename T>
std::string optionalToString(const T& obj)
{
    if constexpr (has_toString<T>)
        return obj.toString();
    else
        return "toString not defined";
}

0

यहाँ काम कर रहे कोड का एक उदाहरण है।

template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());

template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
    return obj->toString();
}

template <class T>
std::string optionalToString(const T* obj, long)
{
    return "toString not defined";
}

int main()
{
    A* a;
    B* b;

    std::cout << optionalToString(a, 0) << std::endl; // This is A
    std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}

toStringFn<T>* = nullptrउस फ़ंक्शन को सक्षम करेगा जो अतिरिक्त intतर्क लेता है जिसमें फ़ंक्शन पर प्राथमिकता होती है जो longकि कॉल करने के साथ होती है 0

trueफ़ंक्शन लागू होने पर जो फ़ंक्शन देता है उसके लिए आप उसी सिद्धांत का उपयोग कर सकते हैं ।

template <typename T>
constexpr bool toStringExists(long)
{
    return false;
}

template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
    return true;
}


int main()
{
    A* a;
    B* b;

    std::cout << toStringExists<A>(0) << std::endl; // true
    std::cout << toStringExists<B>(0) << std::endl; // false
}

0

मुझे भी ऐसी ही समस्या का समाधान करना पड़ा था:

एक टेम्प्लेट क्लास जो कुछ बेस क्लास से ली जा सकती है, कुछ जिसमें एक निश्चित सदस्य और अन्य हैं जो नहीं करते हैं।

मैंने इसे "टाइपोफ़" (निकोला बोनेली के) उत्तर के समान हल किया, लेकिन डिक्टाइप के साथ इसलिए यह संकलन और एमएसवीएस पर सही ढंग से चलता है:

#include <iostream>
#include <string>

struct Generic {};    
struct HasMember 
{
  HasMember() : _a(1) {};
  int _a;
};    

// SFINAE test
template <typename T>
class S : public T
{
public:
  std::string foo (std::string b)
  {
    return foo2<T>(b,0);
  }

protected:
  template <typename T> std::string foo2 (std::string b, decltype (T::_a))
  {
    return b + std::to_string(T::_a);
  }
  template <typename T> std::string foo2 (std::string b, ...)
  {
    return b + "No";
  }
};

int main(int argc, char *argv[])
{
  S<HasMember> d1;
  S<Generic> d2;

  std::cout << d1.foo("HasMember: ") << std::endl;
  std::cout << d2.foo("Generic: ") << std::endl;
  return 0;
}

-1
template<class T>
auto optionalToString(T* obj)
->decltype( obj->toString(), std::string() )
{
     return obj->toString();
}

template<class T>
auto optionalToString(T* obj)
->decltype( std::string() )
{
     throw "Error!";
}

6
"हमें किसी उत्तर विवरण की आवश्यकता नहीं है" ... इसे बेहतर बनाने के लिए कृपया अपने उत्तर में कुछ सूचनात्मक विवरण जोड़ें। धन्यवाद।
YesThatIsMyName
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.