2016-10-12 9 views
9

मैंने एक प्रकार-सुरक्षित आईडी क्लास बनाई है, लेकिन अब मैं ऑपरेटर ++ का समर्थन करना चाहता हूं यदि अंतर्निहित प्रकार में भी है। this और this answears से मैं 2 विकल्प के साथ आए हैं, लेकिन वे दोनों असफल जब सहायता के साथ instantiated:SFINAE केवल क्लास सदस्य होने के लिए

template<typename T, typename TID = unsigned int> 
struct AId { 
    typedef AId<T, TID> type; 
    typedef T handled_type; 
    typedef TID value_type; 

private: 
    value_type id; 

    template<typename _T> struct IsIncrementable 
    { 
    template<typename _U> using rm_ref = typename std::remove_reference<_U>::type; 
    typedef char (&yes)[1]; 
    typedef char (&no)[2]; 
    template<class _U> 
    static yes test(_U *data, typename std::enable_if< 
         std::is_same<_U, rm_ref<decltype(++(*data))>>::value 
        >::type * = 0); 
    static no test(...); 
    static const bool value = sizeof(yes) == sizeof(test((rm_ref<_T> *)0)); 
    }; 

public: 
    explicit AId(const value_type &id) : id(id) {} 

... 

    //This fails with error: no match for 'operator++' (operand type is 
    //'AId<some_type, std::basic_string<char> >::value_type 
    //{aka std::basic_string<char>}') 
    //auto operator++() -> decltype(++id, std::ref(type())) { ++id; return *this; } 
    //       ^
    template<typename = decltype(++id)> 
    auto operator++() -> decltype(++id, std::ref(type())) { ++id; return *this; } 

    //error: no type named 'type' in 'struct std::enable_if<false, int>' 
    template<typename std::enable_if<IsIncrementable<value_type>::value, int>::type = 0> 
    type operator++(int /*postfix*/) { type old(id); ++id; return old; } 
}; 

कैसे AId<> हो सकता है operator++ केवल AId<>::value_type अगर यह भी है? मैं सी ++ 11 तक सीमित हूं और कोई बढ़ावा नहीं देता हूं।

पहले एक दूसरा प्रश्न था जिससे मैंने अपने here पर एक प्रश्न बना दिया था।

हालांकि मैं वास्तव में अपने कोड में @Sam Varshavchik answear का उपयोग कर रहा हूं, लेकिन मुझे लगता है कि @Guillaume Racicot द्वारा प्रदान किया गया एक अधिक सामान्य होने के लिए, इसलिए मैंने इसे एक समाधान के रूप में चुना।

अब मैं @ बैरी के answear का उपयोग कर रहा हूं जो कि सरल और सही दोनों है।

उत्तर

4

के रूप में अन्य जवाब में कहा गया है, वास्तव में, आप कार्य हो कि छोड़ दिया जा सकता है अगर आप उनका उपयोग नहीं करते हैं तो त्रुटियों को तत्काल नहीं किया जाता है।

हालांकि, अगर कोई व्यक्ति यह जांचने का प्रयास करने के लिए sfinae का उपयोग करता है कि आपकी कक्षा operator++ का समर्थन करती है, तो उसकी प्रकार की विशेषता उसे झूठी सकारात्मक देगी, जिससे संभावित संकलन त्रुटियां हो सकती हैं। यदि आप उस उपयोग के मामले का समर्थन करना चाहते हैं, तो आपको बहुत सारे विकल्प नहीं छोड़े जाते हैं। आपको इसे सशर्त रूप से लागू करने की आवश्यकता है।

यदि आप किसी सदस्य के सशर्त कार्यान्वयन चाहते हैं, तो आप विरासत का उपयोग कर सकते हैं।

हम उस विशेषता में sfinae डाल दिया और उसके बाद विशेषता का उपयोग करेगा: (सी ++ 11 संगतता के साथ)

template<typename, typename> 
struct has_increment : std::false_type {}; 

template<typename T> 
struct has_increment<T, void_t<decltype(++std::declval<T>())>> : std::true_type {}; 

प्रकार void_t इस तरह लागू किया जा सकता: अब

// void_t implemented with a struct works better for c++11 compilers 
template<typename...> 
struct voider { using type = void; }; 

template<typename... Ts> 
using void_t = typename voider<Ts...>::type; 

, हम एक मिश्रण में operator++ अपनी कक्षा के कार्यान्वयन में लागू कर सकते हैं:

template<typename Child> 
struct MixinIncrement { 
    auto operator++(int /*postfix*/) { 
     Child::type old(self().id); 
     ++(self().id); 
     return old; 
    } 

private: 
    const Child& self() const { return *static_cast<const Child*>(this); } 
    Child& self() { return *static_cast<Child*>(this); } 
}; 

अब, सशर्त operator++ समारोह को लागू करने, आप हमारी विशेषता के साथ std::conditional उपयोग कर सकते हैं:

struct Dummy {}; 

template<typename Child> 
using Parent = typename std::conditional<has_increment<TID>::value, MixinIncrement<Child>, Dummy>::type; 

template<typename T, typename TID = unsigned int> 
struct AId : Parent<AId<T, TID>> { 
    /* your stuff */ 
}; 

अब, जब से तुम mixin केवल तभी प्रकार प्रकार विशेषता मिलान है फैली हुई है, आप केवल operator++ अगर TID है मिल वृद्धि ऑपरेटर। यदि आप नहीं, तो ऑपरेटर लागू नहीं किया गया है, तो आप Dummy का विस्तार करना समाप्त कर देते हैं।

यह वही चाल अच्छा है यदि आप सशर्त रूप से प्रतिलिपि बनाना और कन्स्ट्रक्टर को स्थानांतरित करना चाहते हैं।

+0

अच्छा answear, लेकिन इसके लिए @Sam Varshavchik की तुलना में अधिक कोड की आवश्यकता है। क्या कोई कारण है कि यह प्राथमिक है? मुझे लगता है कि यह वास्तव में SFINAE का उपयोग करता है, लेकिन यह एक आवश्यकता नहीं है, मैंने सोचा कि यह एकमात्र समाधान था। इसके अलावा, void_t की आपकी परिभाषा के संबंध में, [this] (http://en.cppreference.com/w/cpp/types/void_t) के अनुसार यह वास्तव में 'टेम्पलेट struct make_void {typedef void होना चाहिए टाइप;}; ' ' टेम्पलेट void_t = typename make_void :: टाइप; ' – Olivetree

+0

का उपयोग करके मेरा संपादन देखें। मैंने पहले अनुच्छेद –

+0

में एक कारण जोड़ा है 'voider' या' make_void' नाम का कोई प्रभाव नहीं है। यह किसी भी मानक का हिस्सा नहीं है और उदाहरण में 'make_void'' चुना गया था। अगर नाम बदलने के लिए स्वतंत्र हो जाओ। –

7

मुझे SFINAE की आवश्यकता नहीं है, या किसी भी चीज़ की आवश्यकता नहीं है।

बस अपने operator++ को लागू करें। यदि अंतर्निहित वर्ग इसका समर्थन नहीं करता है, और operator++ आपके टेम्पलेट रैपर के लिए नहीं बुलाया जाता है, तो ऑपरेटर को तत्काल नहीं मिलता है, कोई नुकसान नहीं होता है।

जीसीसी 6.2.1 के साथ परीक्षण किया गया, ध्यान दें कि mytemplate<not_incrementable> नहीं मुद्दों के साथ का दृष्टांत जाएगी, जब तक कुछ भी नहीं की कोशिश करता है के रूप में यह बढ़ाने के लिए:

#include <iostream> 
#include <vector> 

class not_incrementable {}; 

class incrementable { 
public: 

    incrementable &operator++() 
    { 
     return *this; 
    } 
}; 

template<typename T> class mytemplate { 

public: 

    T t; 

    mytemplate<T> operator++() 
    { 
     ++t; 
     return *this; 
    } 
}; 

void foo() 
{ 
    mytemplate<not_incrementable> will_this_compile; 

    mytemplate<incrementable> will_this_compile2; 

    ++will_this_compile2; // Compiles 

    // ++will_this_compile; // Will not compile 
} 
+2

तो टेम्पलेट कक्षाएं किसी समस्या के बिना अपूर्ण हो सकती हैं? : ओ मैंने हमेशा सोचा था कि जब एक टेम्पलेट वर्ग को तत्कालित किया जाता है तो सभी गैर-टेम्पलेट सदस्य फ़ंक्शन को संकलित करने योग्य और स्थिर वर्ग की तरह स्थिर रूप से मौजूद होना चाहिए। क्या आप एक संदर्भ दे सकते हैं जो बताता है कि आपके कोड को किसी भी मानक सी ++ कंपाइलर (और न केवल जी ++) पर संकलित करना चाहिए? – Olivetree

+0

वास्तव में इसके विपरीत, टेम्पलेट कक्षाओं के तरीकों को तत्काल आईएफएफ कहा जाता है; यह वास्तव में आवश्यक है। मेरे पास मानक उद्धरण नहीं है, लेकिन std :: vector पर विचार करें। कॉपी कन्स्ट्रक्टर कैसा दिखता है? –

+0

@ नियरफ्राइडमैन वास्तव में, मैंने कभी भी उस विशेष मामले को महसूस नहीं किया था। लेकिन फिर std :: वेक्टर को आइटम को डिफॉल्ट-कन्स्ट्रक्शन करने की आवश्यकता क्यों होती है? यह केवल तत्वों को प्रारंभ किए बिना आकार बदलने() को लागू करने से बच सकता है, और स्मृति को आवंटित कर सकता है। डिफ़ॉल्ट-प्रारंभिकता के बिना यह आरक्षित() के लिए भी तेज होगा। – Olivetree

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