2012-07-04 17 views
8

मैं एक SFINAE समस्या है लेने नहीं करता है:SFINAE: संकलक विशेष टेम्पलेट वर्ग

निम्न कोड में, मैं चाहता हूँ सी ++ संकलक विशेष functor और प्रिंट "विशेष" लेने के लिए है, लेकिन यह "मुद्रण है सामान्य " बजाय।

#include <iostream> 
#include <vector> 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename T::Vec> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

मैं इसे कैसे ठीक कर सकता हूं ताकि विशिष्ट संरचना स्वचालित रूप से उपयोग की जा सके? नोट मैं सीधे Foo पर Functor struct विशेषज्ञ नहीं करना चाहते हैं, लेकिन मैं सभी प्रकार है कि एक Vec प्रकार पर यह विशेषज्ञ करना चाहते हैं।

पी.एस .: मैं जी का उपयोग कर रहा ++ 4.4.4

+0

'कंपाइलर' टैग को हटा दिया गया है, इसका आमतौर पर संकलन प्रक्रिया के बारे में प्रश्न के लिए उपयोग किया जाता है, जबकि यह प्रश्न सी ++ भाषा के बारे में है। –

उत्तर

11

पिछले जवाब में आप को गुमराह करने के लिए क्षमा करें, मैं एक पल है कि यह आसान होगा के लिए सोचा। तो मैं यहां एक पूरा समाधान प्रदान करने की कोशिश करूंगा। सामान्य दृष्टिकोण को हल करने के लिए समस्या के इस प्रकार एक लक्षण सहायक टेम्पलेट लिख सकते हैं और (या तो सी ++ 11, को बढ़ावा देने या मैनुअल कार्यान्वयन) एक साथ इसका इस्तेमाल enable_if के साथ एक कक्षा विशेषज्ञता तय करने के लिए है:

विशेषता

एक साधारण दृष्टिकोण, जरूरी नहीं कि सबसे अच्छा है, लेकिन सरल लिखने के लिए होगा:

template <typename T> 
struct has_nested_Vec { 
    typedef char yes; 
    typedef char (&no)[2]; 
    template <typename U> 
    static yes test(typename U::Vec* p); 
    template <typename U> 
    static no test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

दृष्टिकोण सरल है, दो टेम्पलेट काम करता है, विभिन्न आकार के है कि वापसी प्रकार प्रदान करते हैं। जिसमें से एक नेस्टेड Vec प्रकार लेता है और दूसरा इलिप्सिस लेता है। उन सभी प्रकारों के लिए जिनके पास नेस्टेड Vec है, पहला अधिभार एक बेहतर मिलान है (किसी भी प्रकार के लिए इलिप्सिस सबसे खराब मिलान है)। उन प्रकारों के लिए है कि एक नेस्टेड Vec SFINAE कि अधिभार और एकमात्र विकल्प छोड़ दिया अंडाकार हो जाएगा छोड़ दी जाएगी जरूरत नहीं है। तो अब हमारे पास यह पूछने का एक गुण है कि किसी भी प्रकार का घोंसला Vec प्रकार है। यदि

आप किसी भी लाइब्रेरी से यह उपयोग कर सकते हैं

सक्षम करें, या आप अपने स्वयं रोल कर सकते हैं, यह काफी सरल है:

template <bool state, typename T = void> 
struct enable_if {}; 

template <typename T> 
struct enable_if<true,T> { 
    typedef T type; 
}; 

जब पहला तर्क false है, आधार टेम्पलेट है एकमात्र विकल्प, और कहा कि एक नेस्टेड type अगर हालत true, enable_if है तो एक नेस्टेड type कि हम SFINAE साथ उपयोग कर सकते हैं नहीं है,।

कार्यान्वयन

अब हम टेम्पलेट और विशेषज्ञता है कि एक नेस्टेड Vec के साथ ही उन प्रकार के लिए SFINAE का उपयोग करेगा प्रदान करने की आवश्यकता:

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
     std::cerr << "general" << std::endl; 
    } 
}; 
template<class T> 
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > { 
    void operator()() const { 
     std::cerr << "special" << std::endl; 
    } 
}; 

जब भी हम एक प्रकार के साथ Functor का दृष्टांत, संकलक विशेषज्ञता, जो बारी में has_nested_Vec का दृष्टांत और एक सच मान प्राप्त होगा, enable_if के लिए पारित उपयोग करने के लिए कोशिश करेंगे। उन प्रकार, जिसका मान false है के लिए, enable_if तो विशेषज्ञता SFINAE में छोड़ दिया जाएगा और आधार टेम्पलेट का उपयोग किया जाएगा एक नेस्टेड type प्रकार नहीं है,।

आपका विशेष मामले

अपने विशेष मामले, ऐसा लगता है जहां कि तुम सच में नहीं है पूरे प्रकार लेकिन सिर्फ ऑपरेटर विशेषज्ञ, आप एक ही एक में तीन तत्वों मिश्रण कर सकते हैं की जरूरत में: एक Functor कि दो आंतरिक टेम्प्लेट की Vec की उपस्थिति पर आधारित कार्यों में से एक के लिए डिस्पैच, enable_if के लिए की जरूरत और लक्षण वर्ग को हटाने:

template <typename T> 
class Functor { 
    template <typename U> 
    void op_impl(typename U::Vec* p) const { 
     std::cout << "specialized"; 
    } 
    template <typename U> 
    void op_impl(...) const { 
     std::cout << "general"; 
    } 
public: 
    void operator()() const { 
     op_impl<T>(0); 
    } 
}; 
2

हालांकि इस एक पुराने सवाल है, मुझे लगता है कि यह अभी भी एक जोड़े को प्रदान करने के लायक है मूल कोड को तुरंत ठीक करने के लिए अधिक विकल्प।

मूल रूप से, समस्या SFINAE (वह हिस्सा ठीक है, वास्तव में है) के उपयोग के साथ नहीं है, लेकिन तर्क आंशिक विशेषज्ञता में आपूर्ति करने के लिए प्राथमिक टेम्पलेट (void) में डिफ़ॉल्ट पैरामीटर का मिलान (typename T::Vec) के साथ । प्राथमिक टेम्पलेट में डिफ़ॉल्ट पैरामीटर के कारण, Functor<Foo> वास्तव में Functor<Foo, void> का अर्थ है। संकलक का दृष्टांत को विशेषज्ञता का उपयोग कर कि कोशिश करता है, यह विशेषज्ञता में लोगों के साथ दो तर्क से मेल करने की कोशिश करता है और विफल रहता है, के रूप में voidstd::vector<int> के लिए प्रतिस्थापित किया जा सकता। यह फिर प्राथमिक टेम्पलेट का उपयोग कर तत्काल करने के लिए वापस गिर जाता है।

तो, तेज ठीक है, जो मानता है अपने सभी Vec रों std::vector<int> रों कर रहे हैं, इस

template<class T, class E = std::vector<int>> 

विशेषज्ञता अब इस्तेमाल किया जाएगा के साथ लाइन

template<class T, class V = void> 

को बदलने के लिए है, क्योंकि तर्क मिलेंगे। सरल, लेकिन बहुत सीमित। स्पष्ट रूप से, हमें विशेषज्ञता में तर्क के प्रकार को बेहतर ढंग से नियंत्रित करने की आवश्यकता है, ताकि इसे किसी ऐसे मैच से मेल किया जा सके जिसे हम प्राथमिक टेम्पलेट में डिफ़ॉल्ट पैरामीटर के रूप में निर्दिष्ट कर सकें। एक त्वरित समाधान नई लक्षण को परिभाषित करने की आवश्यकता नहीं है कि यह है:

#include <iostream> 
#include <vector> 
#include <type_traits> 

template<class T, class E = std::true_type> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

template<class T> 
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

यह किसी भी Vec प्रकार है कि समझ में यहाँ कर सकता है मूल प्रकारों और सरणियों, उदाहरण के लिए, और संदर्भ या उन्हें संकेत भी शामिल है, के लिए काम करेंगे।

1

एक सदस्य प्रकार के अस्तित्व का पता लगाने के लिए एक अन्य विकल्प void_t उपयोग करने के लिए है। वैध आंशिक विशेषज्ञताओं जब तक वे डिफ़ॉल्ट पैरामीटर (रों) से मेल खाते हैं सामान्य कार्यान्वयन के लिए बेहतर हैं, हम एक प्रकार है कि void का मूल्यांकन जब वैध है, और केवल वैध निर्दिष्ट सदस्य मौजूद है चाहते हैं; यह प्रकार आमतौर पर (और, सी ++ 17 के रूप में, कैननिक रूप से) void_t के रूप में जाना जाता है।

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

अपने संकलक ठीक से समर्थन नहीं करता है (जल्दी सी ++ 14 compilers में उर्फ ​​टेम्पलेट्स में अप्रयुक्त मापदंडों ऊपर void_t तोड़ने SFINAE सुनिश्चित करने के लिए गारंटी नहीं थी,), एक समाधान उपलब्ध है।

template<typename... Ts> struct make_void { typedef void type; }; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

सी ++ 17 के रूप में, void_ttype_traits में उपयोगिताओं पुस्तकालय में उपलब्ध है।

#include <iostream> 
#include <vector> 
#include <type_traits> // For void_t. 

template<class T, class V = void> 
struct Functor { 
    void operator()() const { 
    std::cerr << "general" << std::endl; 
    } 
}; 

// Use void_t here. 
template<class T> 
struct Functor<T, std::void_t<typename T::Vec>> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 

struct Foo { 
    typedef std::vector<int> Vec; 
}; 

int main() { 
    Functor<Foo> ac; 
    ac(); 
} 

इसके साथ ही उत्पादन special, के रूप में इरादा है।


इस मामले में, चूंकि हम सदस्य प्रकार के अस्तित्व की जांच कर रहे हैं, प्रक्रिया बहुत सरल है; यह अभिव्यक्ति SFINAE या type_traits लाइब्रेरी के बिना किया जा सकता है, जिससे हमें आवश्यक होने पर सी ++ 03 सुविधाओं का उपयोग करने के लिए चेक को फिर से लिखने की अनुमति मिलती है।

// void_t: 
// Place above Functor's definition. 
template<typename T> struct void_t { typedef void type; }; 

// ... 

template<class T> 
struct Functor<T, typename void_t<typename T::Vec>::type> { 
    void operator()() const { 
    std::cerr << "special" << std::endl; 
    } 
}; 
मेरी जानकारी के लिए

, इस, सभी नहीं तो, SFINAE सक्षम सी ++ 03-, सी ++ 11-, सी ++ 14-, या सी ++ 1Z अनुरूप compilers सबसे पर काम करना चाहिए । मानक के पीछे अंतराल वाले कंपाइलर्स से निपटने के दौरान यह उपयोगी हो सकता है, या प्लेटफ़ॉर्म के लिए संकलन करते समय जिनके पास अभी तक C++ 11-संगत कंपाइलर नहीं हैं।


void_t के बारे में अधिक जानकारी के लिए, cppreference देखते हैं।

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