2017-01-17 15 views
14

std::get साथ अच्छा खेलने get`, SFINAE के अनुकूल होने के लिए प्रतीत नहीं होता है, जैसा कि निम्न परीक्षण का मामला द्वारा दिखाए गए:`बनाना std :: SFINAE

template <class T, class C> 
auto foo(C &c) -> decltype(std::get<T>(c)) { 
    return std::get<T>(c); 
} 

template <class> 
void foo(...) { } 

int main() { 
    std::tuple<int> tuple{42}; 

    foo<int>(tuple); // Works fine 
    foo<double>(tuple); // Crashes and burns 
} 

See it live on Coliru

लक्ष्य हटाने की है दूसरी ओवरलोड की ओर foo पर दूसरा कॉल। अभ्यास में, libstdC++ देता है:

/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/6.3.0/../../../../include/c++/6.3.0/tuple:1290:14: fatal error: no matching function for call to '__get_helper2' 
    { return std::__get_helper2<_Tp>(__t); } 
      ^~~~~~~~~~~~~~~~~~~~~~~ 

libC++ और अधिक प्रत्यक्ष है, एक सीधी static_assert विस्फोट के साथ:

/usr/include/c++/v1/tuple:801:5: fatal error: static_assert failed "type not found in type list" 
    static_assert (value != -1, "type not found in type list"); 
    ^    ~~~~~~~~~~~ 

मैं वास्तव में प्याज की परतों देखना हो C एक std::tuple विशेषज्ञता है, और की तलाश में लागू करने के लिए नहीं की तरह होगा T के लिए इसके पैरामीटर के अंदर ...

क्या std::get का कोई कारण नहीं है SFINAE-friendly होने के लिए? क्या ऊपर उल्लिखित की तुलना में बेहतर कामकाज है?

मुझे something about std::tuple_element मिला है, लेकिन std::get नहीं मिला है।

+0

"एस" "SFINAE" का हिस्सा "प्रतिस्थापन" के लिए खड़ा है । यहां, टेम्पलेट पैरामीटर को पहले 'foo' में प्रतिस्थापित करने में कोई समस्या नहीं थी। 'टी'' डबल' है। 'सी' वह टुपल प्रकार है। प्रतिस्थापन सफल हुआ! –

+3

@SamVarshavchik लेकिन वापसी प्रकार में एक अवैध फ़ंक्शन कॉल बनाना शामिल है। जो प्रतिस्थापन विफलता को ट्रिगर करेगा, अगर 'std :: get' SFINAE- अनुकूल था (यानी अमान्य कॉल के लिए अधिभार सेट से बाहर निकलें)। इसलिए मेरा सवाल है। – Quentin

+0

@ सैमवर्षवचिक [इस स्निपेट को देखें] (http://coliru.stacked-crooked.com/a/f22c57a5c4f2fb37) उदाहरण के लिए। – Quentin

उत्तर

15

std::get<T> स्पष्ट रूप से नहीं SFINAE के अनुकूल है, [tuple.elem] के अनुसार है:

template <class T, class... Types> 
    constexpr T& get(tuple<Types...>& t) noexcept; 
// and the other like overloads 

आवश्यक है: प्रकार T ठीक एक बार Types... में होता है। अन्यथा, कार्यक्रम खराब गठित है।

std::get<I> स्पष्ट रूप से SFINAE- अनुकूल नहीं है।


जहां तक ​​अन्य प्रश्न के रूप में:

वहाँ std::get के लिए SFINAE के अनुकूल नहीं होने के लिए एक कारण है?

पता नहीं। आम तौर पर, यह एक ऐसा बिंदु नहीं है जिसे SFINAE-ed पर होना चाहिए। तो मुझे लगता है कि ऐसा कुछ नहीं माना जाता था जिसे करने की आवश्यकता थी। गैर-व्यवहार्य उम्मीदवार विकल्पों के समूह के माध्यम से स्क्रॉल करने से हार्ड त्रुटियों को समझना बहुत आसान है। यदि आप मानते हैं कि std::get<T> के लिए SFINAE- अनुकूल होने के लिए अनिवार्य कारण होने के कारण, आप इसके बारे में एक एलडब्ल्यूजी मुद्दा जमा कर सकते हैं।

क्या ऊपर उल्लिखित की तुलना में बेहतर कामकाज है?

ज़रूर। आप get की अपनी SFINAE के अनुकूल संस्करण लिख सकते हैं:

template <class T, class... Types, 
    std::enable_if_t<(std::is_same<T, Types>::value + ...) == 1, int> = 0> 
constexpr T& my_get(tuple<Types...>& t) noexcept { 
    return std::get<T>(t); 
} 

और फिर उस के साथ क्या के रूप में आप चाहते हैं।

3

N4527 से (मुझे लगता है कि यह अभी भी मानक में है):

§ 20.4.2.6 (8):

आवश्यक है: प्रकार टी अन्यथा प्रकार में ठीक एक बार होता है ...., कार्यक्रम खराब गठित है।

मानक के अनुसार, ऊपर दिया गया कार्यक्रम बीमार है।

चर्चा का अंत।

5

std::get पर SFINAE न करें; इसकी अनुमति नहीं है।

यहाँ यदि आप कर सकते हैं using std::get; get<X>(t) परीक्षण करने के लिए दो अपेक्षाकृत sfinae अनुकूल तरीके हैं:

template<class T,std::size_t I> 
using can_get=std::integral_constant<bool, I<std::tuple_size<T>::value>; 

namespace helper{ 
    template<class T, class Tuple> 
    struct can_get_type:std::false_type{}; 
    template<class T, class...Ts> 
    struct can_get_type<T,std::tuple<Ts...>>: 
    std::integral_constant<bool, (std::is_same_v<T,Ts>+...)==1> 
    {}; 
} 
template<class T,class Tuple> 
using can_get=typename helpers::can_get_type<T,Tuple>::type; 

फिर अपने कोड में लिखा है:

template <class T, class C, std::enable_if_t<can_get_type<C,T>{},int> =0> 
decltype(auto) foo(C &c) { 
    return std::get<T>(c); 
} 
संबंधित मुद्दे