2013-03-15 12 views
24

मैं पहले से ही SFINAE मुहावरा काफी कुछ बार इस्तेमाल किया और मैं डाल करने के लिए इस्तेमाल किया गया मेरे टेम्पलेट मानकों में बजाय वापसी प्रकार में std::enable_if<>। हालांकि, मैं कुछ मामूली मामले में आया जहां यह काम नहीं किया, और मुझे यकीन नहीं है कि क्यों। सबसे पहले, यहाँ है अपने मुख्य:SFINAE वापसी प्रकार में काम कर रहे हैं, लेकिन नहीं टेम्पलेट पैरामीटर के रूप में

template<typename T, 
     typename = typename std::enable_if<std::is_integral<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename = typename std::enable_if<std::is_floating_point<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

और यहाँ कोड का एक माना जाता है कि बराबर टुकड़ा है कि ठीक काम करता है है:

int main() 
{ 
    foo(5); 
    foo(3.4); 
} 

यहाँ foo का कार्यान्वयन और त्रुटि से चलाता है

template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_integral<T>::value>::type 
{ 
    std::cout << "I'm an integrer!\n"; 
} 

template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_floating_point<T>::value>::type 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

मेरा प्रश्न है: क्यों, जबकि दूसरा एक यह ट्रिगर नहीं करता foo के पहले कार्यान्वयन से चलाता है कि त्रुटि है?

main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)' 
auto foo(T) 
    ^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here 
auto foo(T) 
    ^
main.cpp: In function 'int main()': 
main.cpp:23:12: error: no matching function for call to 'foo(double)' 
    foo(3.4); 
      ^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T) 
auto foo(T) 
    ^
main.cpp:6:6: note: template argument deduction/substitution failed: 
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>' 
      typename = typename std::enable_if<std::is_integral<T>::value>::type> 
     ^

संपादित:

Working code और faulty code

+1

ठीक है। वास्तविक प्रदर्शन: [भाग संकलित करने में पहला असफल] (http://ideone.com/mJ8Zp6) और [2 सफलतापूर्वक संकलित भाग] (http://ideone.com/G0jBft)। –

+0

अतिरिक्त जानकारी: वीएस 2012 नवंबर सीटीपी के साथ ही। –

+3

[यह] (http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html) आप के लिए एकदम सही पढ़ा जाना चाहिए। – Xeo

उत्तर

26

आप 14.5.6.1 Function template overloading पर एक नज़र (सी ++ 11 मानक) जहां समारोह टेम्पलेट्स समानक परिभाषित किया गया है रखना चाहिए। संक्षेप में, डिफ़ॉल्ट टेम्पलेट तर्कों पर विचार नहीं किया जाता है, इसलिए पहले मामले में आपके पास एक ही फ़ंक्शन टेम्पलेट दो बार परिभाषित किया गया है। दूसरे मामले में आपके पास वापसी प्रकार में उपयोग किए गए अभिव्यक्ति संदर्भित टेम्पलेट पैरामीटर हैं (फिर 14.5.6.1/4 देखें)। चूंकि यह अभिव्यक्ति हस्ताक्षर का हिस्सा है, इसलिए आपको दो अलग-अलग फ़ंक्शन टेम्पलेट घोषणाएं मिलती हैं और इस प्रकार SFINAE को काम करने का मौका मिलता है।

+0

बहुत बहुत धन्यवाद। यह स्पष्टीकरण कम से कम सरल और स्पष्ट है। मुझे इस नियम का कोई अंदाज़ा नहीं था :) – Morwenn

7

टेम्पलेट के = ... सिर्फ एक डिफ़ॉल्ट पैरामीटर देता है। यह वास्तविक हस्ताक्षर जो

template<typename T, typename> 
auto foo(T a); 

तरह के लिए दोनों कार्यों लग रहा है का हिस्सा नहीं है।

अपनी आवश्यकताओं के आधार पर, इस समस्या के लिए सबसे सामान्य समाधान टैग भेजने उपयोग कर रहा है।

टेम्पलेट्स में
struct integral_tag { typedef integral_tag category; }; 
struct floating_tag { typedef floating_tag category; }; 

template <typename T> struct foo_tag 
: std::conditional<std::is_integral<T>::value, integral_tag, 
        typename std::conditional<std::is_floating_point<T>::value, floating_tag, 
               std::false_type>::type>::type {}; 

template<typename T> 
T foo_impl(T a, integral_tag) { return a; } 

template<typename T> 
T foo_impl(T a, floating_tag) { return a; } 

template <typename T> 
T foo(T a) 
{ 
    static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value, 
       "T must be either floating point or integral"); 
    return foo_impl(a, typename foo_tag<T>::category{}); 
} 

struct bigint {}; 
template<> struct foo_tag<bigint> : integral_tag {}; 

int main() 
{ 
    //foo("x"); // produces a nice error message 
    foo(1); 
    foo(1.5); 
    foo(bigint{}); 
} 
+2

यह बहुत सामान्य नहीं है - यह इस सटीक मामले के लिए विशिष्ट है - पूर्णांक बनाम पूर्णांक। – einpoklum

2

मान काम:

template<typename T, 
     typename std::enable_if<std::is_integral<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 
संबंधित मुद्दे

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