2012-02-14 20 views
7

मैं चल बिन्दु प्रकार (निम्नतम स्तर पर सिर्फ एक uint16_t) एक आधा सटीक करने के लिए विभिन्न प्रकार के अंकगणित परिवर्तित करने के लिए कार्य करता है और मैं पूर्णांक के लिए विभिन्न कार्यों और चल बिन्दु स्रोत प्रकार, SFINAE और std::enable_if का उपयोग कर :SFINAE भेदभाव पर हस्ताक्षर किए और अहस्ताक्षरित

template<typename T> 
uint16_t to_half(typename std::enable_if< 
       std::is_floating_point<T>::value,T>::type value) 
{ 
    //float to half conversion 
} 

template<typename T> 
uint16_t to_half(typename std::enable_if< 
       std::is_integral<T>::value,T>::type value) 
{ 
    //int to half conversion 
} 

ये स्पष्ट इन्स्टेन्शियशन द्वारा एक सार्वभौमिक टेम्प्लेट की निर्माता से आंतरिक रूप से कहा जाता है:

template<typename T> 
half::half(T rhs) 
    : data_(detail::conversion::to_half<T>(rhs)) 
{ 
} 

यह संकलित करता है तथा यह भी ठीक काम करता है। अब मैं पर हस्ताक्षर किए और अहस्ताक्षरित पूर्णांकों के बीच अंतर करने, दो कार्यों के साथ दूसरे समारोह की जगह का प्रयास करें:

template<typename T> 
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value && 
       std::is_signed<T>::value,T>::type value) 
{ 
    //signed to half conversion 
} 

template<typename T> 
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value && 
       std::is_unsigned<T>::value,T>::type value) 
{ 
    //unsigned to half conversion 
} 

लेकिन एक बार मैं संकलन करने की कोशिश इस VS2010 मुझे

त्रुटि C2995 देता है: "uint16_t math::detail::conversion::to_half(std::enable_if<std::tr1::is_integral<_Ty>::value && std::tr1::is_signed<_Ty>::value, T>::type)": समारोह टेम्पलेट पहले ही परिभाषित किया गया है।

तो ऐसा लगता है कि यह दो टेम्पलेट्स के बीच असंबद्ध नहीं हो सकता है, लेकिन स्पष्ट रूप से फ्लोटिंग पॉइंट संस्करण के साथ अभिन्न संस्करण के साथ कोई समस्या नहीं थी।

लेकिन चूंकि मैं इतना टेम्पलेट जादूगर नहीं हूं, इसलिए मैं यहां कुछ स्पष्ट याद कर रहा हूं (या शायद यह वास्तव में काम करना चाहिए और केवल एक वीएस -2010 बग है)। तो यह क्यों काम नहीं करता है और इसे यथासंभव कुछ प्रोग्रामिंग ओवरहेड के साथ और मानक-केवल सुविधाओं (यदि संभव हो) की सीमाओं में भी कैसे बनाया जा सकता है?

+1

यह स्पष्ट नहीं है कि 'is_signed' /' is_unsigned' पारस्परिक रूप से अनन्य है (हैलो 'char'?)। दूसरा संस्करण बनाने का प्रयास करें '! Std :: is_signed :: value' इसके बजाय। –

+0

क्या आप सदस्यों में से किसी एक के लिए 'std :: is_signed :: value' का उपयोग करने का प्रयास कर सकते हैं और'! Std :: is_signed :: दूसरे के लिए मूल्य'? यह सुनिश्चित करने के लिए है कि केवल कुछ प्रकार नहीं हैं जिनमें 'is_signed' और' is_unsigned' के लिए असंगत सेटिंग्स हैं। –

+0

@ केरेकस्क और बीटमार हा, उसने ऐसा किया! विश्वास नहीं कर सकता था कि यह इतना आसान था। अगर कोई इसे उत्तर के रूप में जोड़ता है तो मैं इसे स्वीकार करूंगा। –

उत्तर

3

यदि यह काम नहीं करता है तो आपका कंपाइलर त्रुटि में है।

टेम्प्लेट पैरामीटर शामिल दो भाव बराबर माना जाता है अगर दो कार्यशील परिभाषाएँ भाव युक्त एक परिभाषा शासन संतुष्ट हैं ...

सबसे महत्वपूर्ण नियम यहाँ पर विचार करने के लिए है कि (के विवरण बाहर छोड़ दिया "...")। आपके दो टेम्पलेट्स ओडीआर को संतुष्ट नहीं करते हैं क्योंकि उनके टोकन अनुक्रम भिन्न होते हैं।

दो समारोह टेम्पलेट्स बराबर हैं अगर वे एक ही दायरे में घोषित किया गया है, एक ही नाम है, समान टेम्पलेट पैरामीटर सूचियों, और बदले प्रकार और पैरामीटर सूचियों कि नियमों ऊपर वर्णित का उपयोग कर से जुड़े भाव की तुलना करने के बराबर हैं टेम्पलेट पैरामीटर।

तो आपके दो टेम्पलेट विभिन्न टेम्पलेट्स को परिभाषित करते हैं और संघर्ष नहीं करते हैं। अब आप जांच सकते हैं कि आपके टेम्पलेट्स शायद "कार्यात्मक रूप से समकक्ष" हैं या नहीं। वे टेम्पलेट तर्कों के किसी भी संभावित सेट के लिए होंगे, तो आपकी enable_if अभिव्यक्ति हमेशा एक ही मूल्य उत्पन्न करेगी। लेकिन चूंकि यह is_unsigned और is_signed के लिए सच नहीं है, यह मामला भी नहीं है। यदि ऐसा होता है, तो आपका कोड खराब हो जाएगा, लेकिन बिना निदान की आवश्यकता के (जो प्रभावी रूप से "अपरिभाषित व्यवहार" का अर्थ है)।

+0

'is_unsigned' को बदलकर'! Is_signed' (या इसके विपरीत) काम किया है, इसलिए मुझे लगता है (हालांकि भाषा की कल्पना की गहराई में अच्छी तरह से ज्ञात नहीं है, टेम्पलेट्स के बारे में बात न करने के लिए) कि कुछ प्रकार है जो दोनों हस्ताक्षरित हैं और हस्ताक्षर किए। या ऐसा इसलिए हो सकता है क्योंकि उन टेम्पलेट्स के डिफ़ॉल्ट संस्करण दोनों गैर-अंकगणितीय प्रकारों के लिए 'झूठी' का मूल्यांकन करते हैं? लेकिन फिर फिर, चीजों को असंबद्ध करने के लिए 'is_integral' भी है (अभिन्न प्रकारों के लिए यह shoudl परस्पर अनन्य हो, है ना?)। –

+0

@ क्रिस्टियन मुझे नहीं पता कि वे क्या कर रहे हैं, लेकिन यह निश्चित रूप से गलत व्यवहार है। '! Is_signed' के बजाय' !! is_unsigned' आज़माएं। मैं इसे "काम करने" को देखकर आश्चर्यचकित नहीं होगा :) –

+0

हां, यह भी काम करता है। ठीक है अब मुझे लगता है कि यह वास्तव में थोड़ा हास्यास्पद हो जाता है। आप शायद सही हैं कि संकलक यहां गलती है। –

7

व्यक्तिगत रूप से, मैं SFINAE यहाँ जितना संभव हो उतना से बचने के बाद से आप अधिक भार के साथ एक ही बात को पूरा कर सकते हैं:

template<typename T> 
uint16_t to_half_impl(T val, std::true_type, std::true_type) 
{ 
    // is_integral + is_signed implementation 
} 

template<typename T> 
uint16_t to_half_impl(T val, std::true_type, std::false_type) 
{ 
    // is_integral + is_unsigned implementation 
} 

template<typename T> 
uint16_t to_half_impl(T val, std::false_type, std::true_type) 
{ 
    // is_floating_point implementation 
} 

template<typename T> 
typename std::enable_if<std::is_arithmetic<T>::value, uint16_t>::type to_half(T val) 
{ 
    return to_half_impl(val, std::is_integral<T>(), std::is_signed<T>()); 
} 
+0

+1 अच्छा विकल्प। लेकिन एक साइड नोट के रूप में मुझे लगता है कि फ्लोट संस्करण का अंतिम तर्क 'std :: true_type' होना चाहिए, क्योंकि फ्लोट हमेशा हस्ताक्षरित होते हैं (कम से कम सामान्य कार्यान्वयन, गैर-आईईईई के लिए मेरा रूपांतरण कोड वैसे भी काम नहीं करेगा)। –

+0

@ क्रिस्टियन: आप पूरी तरह से सही हैं; मैं 'is_signed' के लिए एमएसडीएन दस्तावेज़ों का तर्क बंद कर रहा था, जो गलत (आश्चर्य, आश्चर्य) होता है। फिक्स्ड। – ildjarn

1

अधिक आम मुहावरा तर्क के बजाय वापसी प्रकार पर SFINAE उपयोग करने के लिए है प्रकार। अन्यथा, टेम्पलेट प्रकार T deducible नहीं हो सकता है।

// from C++14 
template<bool C, typename T> using enable_if_t = typename std::enable_if<C,T>::type; 

template<typename T> 
enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, uint16_t> 
to_half(T value) 
{ 
    //signed to half conversion 
} 

template<typename T> 
enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value, int16_t> 
to_half(T value) 
{ 
    //unsigned to half conversion 
} 

प्रकार T निम्नलिखित बयान में साथ

auto y=to_half(x); // T is deduced from argument, no need for <T> 

निगम्य है (यहां तक ​​कि तुच्छता), लेकिन अपने मूल कोड के लिए यह नहीं है! वास्तव में, जब बजना के माध्यम से अपने to_half() कार्यान्वयन के साथ इस बयान चल देता है

test.cc:24:11: error: no matching function for call to 'to_half' 
    auto x= to_half(4); 
      ^~~~~~~ 
test.cc:7:10: note: candidate template ignored: couldn't infer template argument 'T' 
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value && 
     ^
test.cc:15:10: note: candidate template ignored: couldn't infer template argument 'T' 
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value && 
     ^
बेशक

, अगर एक स्पष्ट रूप से टेम्पलेट तर्क प्रदान करता है (तुमने किया था के रूप में), इस समस्या को प्रकट नहीं होता है। तो आपका कोड गलत नहीं था (लेकिन संकलक), लेकिन यदि आप टेम्पलेट तर्क प्रकार पास करते हैं तो SFINAE का बिंदु क्या है?

+0

क्या यह तर्कसंगत प्रकार में SFINAE के समान अस्पष्टता समस्या नहीं होनी चाहिए? क्या कोई नियम है जो यह काम करता है लेकिन तर्क संस्करण नहीं है? अन्यथा यह सवाल इतनी ज्यादा प्रतिकूल प्रतीत नहीं होता है। –

+0

एक ** बहुत अच्छा कारण ** है कि तर्क प्रकार पर SFINAE का उपयोग न करें, संपादित उत्तर देखें। – Walter

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