2012-08-28 16 views
6

के लिए SFINAE मैं MyClass कि एक तर्क लेने के लिए और मैं यह केवल संकलित करने के लिए करता है, तो तर्क एक pointer या एक iterator है (कुछ iterator_traits वाले) चाहते हैं के लिए एक निर्माता लिखने के लिए करना चाहते हैं। इसे कैसे प्राप्त करें?std :: enable_if या iterator या सूचक

उत्तर

11

अफसोस की बात है कि यह पता लगाने का कोई मानक तरीका नहीं है कि कक्षा मॉडल Iterator है या नहीं। सबसे सरल जांच यह होगी कि *it और ++it दोनों वाक्य रचनात्मक रूप से मान्य हैं;

template<typename T, 
    typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())> 
    MyClass(T); 

24.2.2 से Iterator आवश्यकताओं को ध्यान में रखते:: 2: आप मानक SFINAE तकनीकों का उपयोग कर ऐसा कर सकते हैं

template<typename T> typename std::enable_if< 
    !std::is_void<decltype(*std::declval<T &>())>::value 
    && std::is_same<decltype(++std::declval<T &>()), 
        typename std::add_lvalue_reference<T>::type>::value, 
    std::true_type>::type has_iterator_requirements_helper(int); 
template<typename T> std::false_type has_iterator_requirements_helper(...); 
template<typename T> struct has_iterator_requirements: 
    decltype(has_iterator_requirements_helper<T>(0)) {}; 

template<typename, bool> struct is_iterator_check: std::false_type {}; 
template<typename T> struct is_iterator_check<T, true>: std::true_type { 
    typedef typename std::iterator_traits<T>::difference_type difference_type; 
    typedef typename std::iterator_traits<T>::value_type value_type; 
    typedef typename std::iterator_traits<T>::iterator_category iterator_category; 
    typedef typename std::iterator_traits<T>::reference reference; 
    typedef typename std::iterator_traits<T>::pointer pointer; 
    static_assert(std::is_same<decltype(*std::declval<T &>()), reference>::value 
     || std::is_void<reference>::value, "*r must be of type reference"); 
}; 
template<typename T> struct is_iterator: is_iterator_check<T, 
    (std::is_pointer<T>::value 
    && !std::is_void<typename std::remove_pointer<T>::type>::value 
    && !std::is_function<typename std::remove_pointer<T>::type>::value 
    ) || (std::is_copy_constructible<T>::value 
    && std::is_copy_assignable<T>::value 
    && std::is_nothrow_destructible<T>::value 
    // TODO: check lvalues are swappable 
    && has_iterator_requirements<T>::value 
    )> {}; 

उपयोग करने के लिए iterator_traits है कि यह सभी के लिए परिभाषित किया गया एक टेम्पलेट है है की कोशिश कर के साथ समस्या प्रकार, और इसका त्वरण एक गैर-SFINAE संदर्भ में विफल हो जाएगा (याद रखें कि SFINAE केवल प्रत्यक्ष प्रतिस्थापन विफलता के लिए लागू होता है)। libstdC++ में conforming extension है जिससे गैर-पुनरावर्तक प्रकारों पर iterator_traits को तुरंत खाली करने का एक खाली प्रकार उत्पन्न होगा; आप प्रकार पर iterator_category के अस्तित्व के लिए जाँच करके एक समान चाल कर सकते हैं:

template<typename T> std::true_type has_iterator_category_helper(
    T::iterator_category *); 
template<typename T> std::false_type has_iterator_category_helper(...); 
template<typename T> struct has_iterator_category<T>: 
    decltype(has_iterator_category_helper<T>(0)) { }; 
template<typename T> struct is_iterator: std::integral_constant<bool, 
    std::is_pointer<T>::value || has_iterator_category<T>::value> {}; 

template<typename T, typename = std::enable_if<is_iterator<T>::value>> 
    MyClass(T); 

हालांकि यह प्रकार है कि खुद को iterator_category का पर्दाफाश नहीं है, लेकिन एक अलग iterator_traits विशेषज्ञता द्वारा रूपांतरित किया गया है के लिए काम नहीं करेगा; उस स्थिति में सरल SFINAE विधि अधिक समझ में आता है (और आप यह पुष्टि करने के लिए कि यह प्रकार इटरेटर जैसा है) आप iterator_traits को कन्स्ट्रक्टर के भीतर तत्काल कर सकते हैं।

+0

@LucDanton सहमत हुए, मैंने SFINAE को एक पसंदीदा तकनीक के रूप में रखा है। – ecatmur

+5

ग्रेट उत्तर, आपने मुझे एक्सटेंशन को विस्तारित किया है जो हम libstdC++ में उपयोग करते हैं :) उस एक्सटेंशन की पृष्ठभूमि के लिए http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497 देखें। 'Iterator_category' की जांच के विचार के लिए क्रेडिट Alisdair Meredith में जाता है। –

+0

मम्म, '* it' की आवश्यकता यह है कि प्रकार' std :: iterator_traits :: संदर्भ' होना चाहिए; यह नहीं कि यह एक संदर्भ प्रकार है (कम से कम इटरेटर के लिए)। लेकिन आप SFINAE को गड़बड़ करने के डर के लिए 'std :: iterator_traits' का उपयोग नहीं कर सकते ... मैं आपको इसे ठीक करने दूंगा! (आपको कुछ अभिव्यक्तियों की मूल्य श्रेणी के साथ फिर भी समस्याएं आती हैं, उदाहरण के लिए '++ std :: declval () ',' टी' नहीं।) –

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