2017-06-06 14 views
16

का पता लगाने के रूप में मुहावराअगर कॉन्स्टेक्सप्रांड की अनुमति नहीं है तो sfinae क्यों है?

template<typename T, typename = void> 
struct has_foo {static constexpr bool value = false;}; 
template<typename T> 
struct has_foo<T, std::void_t<decltype(&T::foo)>> {static constexpr bool value = true;}; 
template<typename T> 
constexpr bool has_foo_v = has_foo<T>::value; 

इस प्रकार है और फिर हम किसी भी प्रकार T में foo की उपस्थिति का पता लगाने कर सकते हैं काम करता है।

if constexpr(has_foo_v<decltype(var)>) 
    var.foo(); 

मेरे समस्या है, कि टाइप करने के लिए काफी एक बहुत (पढ़ें: अभाव अपने कीबोर्ड एक बहुत टाइप करने के लिए नष्ट करने के लिए) है, और मैं अगर निम्न संभव है

if constexpr(std::void_t<decltype(&decltype(var)::foo)>(), true) 
    var.foo(); 

ऐसा नहीं है सोचा ।

क्या इसके पीछे कोई कारण है?
अधिक विशेष रूप से, यदि इस अनुमति की अनुमति दी गई तो क्या व्यापार-बंद किए जाने होंगे?

+9

[प्रासंगिक] (https://meta.stackoverflow.com/a/323382/2069064)। एक जवाब हो सकता है क्योंकि यहां कोई प्रतिस्थापन नहीं है, तो SFINAE कैसे हो सकता है? एक और जवाब हो सकता है क्योंकि यह विचार नहीं किया गया था क्योंकि प्रस्ताव हमेशा 'बूल' लेने के बारे में थे? – Barry

+0

@ बररी ने उम्मीदवार को एक ऐसे रूप में संपादित किया जिसका स्पष्ट जवाब हो सकता है। मूल रूप से [इस तरह के उत्तर] के आधार पर कुछ के बारे में सोच रहा था (https://stackoverflow.com/a/6623089/4832499) –

उत्तर

6

सदस्य कार्य करने के लिए सूचक का आपके द्वारा उपयोग एक बुरा विचार है, यदि foo ओवरलोड हो गया है, तो यह बहुत खराब हो जाता है (आपके पास एक foo है, लेकिन केवल एक नहीं)। वास्तव में कौन चाहता है "क्या आपके पास बिल्कुल एक फू है"? लगभग कोई नहीं।

यहाँ एक briefer संस्करण है:

template<class T> 
using dot_foo_r = decltype(std::declval<T>().foo()); 

template<class T> 
using can_foo = can_apply<dot_foo_r, T>; 

जहां

namespace details { 
    template<template<class...>class, class, class...> 
    struct can_apply:std::false_type{}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = details::can_apply<Z, void, Ts...>; 

अब, लेखन dot_foo_r थोड़ा कष्टप्रद है।

constexpr लैम्बडास के साथ हम इसे कम परेशान कर सकते हैं और इसे इनलाइन कर सकते हैं।

#define RETURNS(...) \ 
    noexcept(noexcept(__VA_ARGS__)) \ 
    -> decltype(__VA_ARGS__) \ 
    { return __VA_ARGS__; } 

यह जरूरत RETURNS मैक्रो, कम से कम जब तक @[](auto&&f)RETURNS(f()) को बैरी प्रस्तुत [](auto&&f)=>f() के बराबर हो नहीं करता है।

हम तो can_invoke_f, जो std::is_invokable के constexpr संस्करण है लिखें:

template<class F> 
constexpr auto can_invoke(F&& f) { 
    return [](auto&&...args)->std::is_invokable<F(decltype(args)...)>{ 
    return {}; 
    }; 
} 

यह हमें देता है:

if constexpr(
    can_invoke([](auto&&var) RETURNS(var.foo()))(var) 
) { 
    var.foo(); 
} 

या का उपयोग करते हुए @ बैरी प्रस्तावित सी ++ 20 वाक्य रचना:

if constexpr(can_invoke(var=>var.foo())(var)) { 
    var.foo(); 
} 

और हम कर रहे हैं।

चाल यह है कि RETURNS मैक्रो (या => सी ++ 20 फीचर) हमें अभिव्यक्ति पर SFINAE करने देता है। लैम्ब्डा उस अभिव्यक्ति को मूल्य के रूप में ले जाने का एक आसान तरीका बन जाता है।

आप

[](auto&&var) ->decltype(var.foo()) { return var.foo(); } 

लिख सकता है लेकिन मुझे लगता है RETURNS इसके लायक है (और मैं मैक्रो पसंद नहीं है)।

+2

बस इस मामले को शामिल करने के बारे में सोच रहा था अगले मसौदे में एक अलग तरह के प्रेरक उदाहरण के रूप में। 'अगर constexpr (can_invoke_f (x => x.foo())) {...} 'ओपी की तुलना में अभी भी बिल्कुल आदर्श नहीं है, लेकिन यह बुरा नहीं है। – Barry

+0

@ बेरी ने 'अस्वीकरण' से छुटकारा पा लिया और 'can_invoke' में सुधार किया; 'can_invoke' अब फ़ंक्शन इंस्टेंस 'एफ' लेता है और' f' पर' constexpr' invoke-tester देता है। चोरी/अनुकूलित करने के लिए स्वतंत्र फ्री; मुझे लगता है कि अब यह बहुत धीमी है। – Yakk

+3

'=>' वाक्यविन्यास क्या है? –

12

के बाद से ग यदि आप वास्तव में sfinae इनलाइन करने की ज़रूरत ++ 17 वहाँ हमेशा एक constexpr लैम्ब्डा समाधान नहीं है:

#include <utility> 

template <class Lambda, class... Ts> 
constexpr auto test_sfinae(Lambda lambda, Ts&&...) 
    -> decltype(lambda(std::declval<Ts>()...), bool{}) { return true; } 
constexpr bool test_sfinae(...) { return false; } 

template <class T> 
constexpr bool bar(T var) { 
    if constexpr(test_sfinae([](auto v) -> decltype(v.foo()){}, var)) 
     return true; 
    return false; 
} 

struct A { 
    void foo() {} 
}; 

struct B { }; 

int main() { 
    static_assert(bar(A{})); 
    static_assert(!bar(B{})); 
} 

[live demo]

+0

ध्यान दें कि '()' 'lvalue संदर्भ है, इसलिए यदि आप जिस फ़ंक्शन का परीक्षण करते हैं गैर-कॉन्स लैवल्यू संदर्भ स्वीकार नहीं करता है, परीक्षण विफल हो जाएगा। –

+0

@ इगोरआर। आप सही हैं लेकिन हमारे पास यह रोमांच है कि हम लैम्ब्डा 'test_sfinae' बना रहे हैं परीक्षण करेंगे। फिर भी मुझे इसके बारे में सोचना होगा क्योंकि यह सही होने से बहुत दूर है। –

+1

@IgorR। मैंने 'अस्वीकरण' में संदर्भ हटा दिया है, अब यह लैम्ब्डा निर्माता तक है यदि वह संदर्भों या मूल्यों पर काम करना चाहता है। यदि कॉपी कन्स्ट्रक्टर स्पष्ट रूप से हटा दिया गया है तो लैम्ब्डा केवल तभी मेल खाएगा जब संदर्भ स्वीकार किया जाता है यानी [[] (ऑटो और वी) ',' [] (कॉन्स ऑटो और वी) 'या' [] (ऑटो और वी) ' –

1

आप std::is_detected का उपयोग कर कोड की मात्रा को भी कम कर सकते हैं।

अपने उदाहरण में, कोड तो लगेगा जैसे:

template <class T> 
using has_foo_t = decltype(std::declval<T>().foo()); 

if constexpr(std::is_detected_v<has_foo_t,decltype(var)>) 
    var.foo(); 
+0

से ऊपर देखें यह काम करता है! :) हालांकि क्या आप वाकई सी ++ 17 फीचर हैं? –

+1

@ डब्ल्यूएफ .: यह एक प्रयोगात्मक सुविधा है। यह अभी तक मानकीकृत नहीं है। – AndyG

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

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