2016-06-21 12 views
3

das folgende Stück Code zu haben:Warum std :: is_function gibt false für einfache Funktionen und Lambdas zurück?

#include <iostream> 
#include <type_traits> 

template <typename F, 
      typename = typename std::enable_if< 
               std::is_function<F>::value 
              >::type> 
int fun(F f) // line 8 
{ 
    return f(3); 
} 

int l7(int x) 
{ 
    return x%7; 
} 

int main() 
{ 
    auto l = [](int x) -> int{ 
    return x%7; 
    }; 
    fun(l); // line 23 
    //fun(l7); this will also fail even though l7 is a regular function 

    std::cout << std::is_function<decltype(l7)>::value ; // prints 1 
} 

ich die folgende Fehlermeldung erhalten:

main2.cpp: In function ‘int main()’: 
main2.cpp:23:8: error: no matching function for call to ‘fun(main()::<lambda(int)>&)’ 
    fun(l); 
     ^
main2.cpp:8:5: note: candidate: template<class F, class> int fun(F) 
int fun(F f) 
    ^
main2.cpp:8:5: note: template argument deduction/substitution failed: 
main2.cpp:5:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’ 
      typename = typename std::enable_if< 
     ^

Wenn ich die std::enable_if Template-Parameter kommentieren Sie dann kompiliert und läuft gut. Warum?

+1

'std :: is_function' prüft nur auf Funktionstypen, die keine lambdas enthalten. Gibt es einen Grund, warum Sie SFINAE verwenden müssen? Wenn Sie wirklich die Überprüfung durchführen möchten, können Sie überprüfen, ob 'f (3)' wohlgeformt ist, anstatt zu überprüfen, ob 'f' funktional ist. – TartanLlama

+0

Sieht so aus, als ob Sie tatsächlich nach etwas wie [' std :: is_callable'] (http://en.cppreference.com/w/cpp/types/is_callable), das mit C++ 17 verfügbar ist, aber "kann in Bezug auf' std :: is_convertible' und 'std :: result_of' "mit reinem C++ 11. – m8mble

+0

@TartanLlama Wie würden Sie prüfen, ob 'f (3)' gut gebildet ist? – Patryk

Antwort

6

Von cppreference:

Checks whether T is a function type. Types like std::function , lambdas, classes with overloaded operator() and pointers to functions don't count as function types.

This answer erklärt, dass müssen Sie auch std::remove_pointer<F>::type als Typ verwenden, da Funktionen Zeiger auf Funktionen umgewandelt werden, wenn sie von Wert übergeben. Ihr Code sollte also wie folgt aussehen:

+0

Also sollte zumindest 'f (l7)' funktionieren, oder? Da 'l7' ist eine reguläre Funktion – Patryk

+0

Korrekt, und aus Ihrem Code sehen Sie bereits die richtige Ausgabe für 'std :: is_function ' – Kevin

+0

Aber es funktioniert nicht http://cpp.sh/96c2b – Patryk

3

Eine weitere Möglichkeit, dieses Problem anzugehen, besteht darin, eine spezifischere Typeigenschaft zu schreiben. Dieser überprüft beispielsweise, ob die Argumenttypen konvertierbar sind und für alles, was aufrufbar ist, funktionieren.

#include <iostream> 
#include <type_traits> 
#include <utility> 
#include <string> 

template<class T, class...Args> 
struct is_callable 
{ 
    template<class U> static auto test(U*p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type()); 

    template<class U> static auto test(...) -> decltype(std::false_type()); 

    static constexpr auto value = decltype(test<T>(nullptr))::value; 
}; 

template<class T, class...Args> 
static constexpr auto CallableWith = is_callable<T, Args...>::value; 


template <typename F, 
std::enable_if_t< 
CallableWith<F, int> 
>* = nullptr 
> 
int fun(F f) // line 8 
{ 
    return f(3); 
} 

int l7(int x) 
{ 
    return x%7; 
} 

int main() 
{ 
    auto l = [](int x) -> int{ 
     return x%7; 
    }; 

    std::cout << "fun(l) returns " << fun(l) << std::endl; 

    std::cout << CallableWith<decltype(l7), int> << std::endl; // prints 1 
    std::cout << CallableWith<decltype(l7), float> << std::endl; // prints 1 because float converts to int 
    std::cout << CallableWith<decltype(l7), const std::string&> << std::endl; // prints 0 
}