2016-06-21 2 views
3

Имея следующий фрагмент кода:Почему std :: is_function возвращает false для простых функций и лямбда?

#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 
} 

Я получаю следующее сообщение об ошибке:

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< 
     ^

Когда я закомментировать параметр std::enable_if шаблона, то он компилирует и работает просто отлично. Зачем?

+1

'std :: is_function' проверяет только типы функций, которые не включают лямбда. Есть ли причина, по которой вам нужно использовать SFINAE? Если вы действительно хотите сделать чек, вы можете проверить, что 'f (3)' правильно сформирован, а не проверяет, является ли функция 'f' функцией- – TartanLlama

+0

Похоже, вы действительно ищете что-то вроде' 'std :: is_callable'] (http://en.cppreference.com/w/cpp/types/is_callable), который доступен с C++ 17, но «может быть реализован в терминах« std :: is_convertible »и' std :: result_of' "с чистым C++ 11. – m8mble

+0

@TartanLlama Как бы вы проверили, правильно ли сформирован 'f (3)'? – Patryk

ответ

6

От 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 объясняет, что вы также должны использовать std::remove_pointer<F>::type как типа, так как функции преобразуются в указатели на функции при передаче по значению. Таким образом, ваш код должен выглядеть так:

template <typename F, 
      typename = typename std::enable_if< 
               std::is_function< 
               typename std::remove_pointer<F>::type 
               >::value 
              >::type> 
int fun(F f) 
{ 
    return f(3); 
} 
+0

Так что, по крайней мере, 'f (l7)' должен работать, правильно? Поскольку 'l7' является регулярной функцией – Patryk

+0

Исправить, и из вашего кода вы уже видите правильный вывод для' std :: is_function ' – Kevin

+0

Но это не работает http://cpp.sh/96c2b – Patryk

3

Еще один способ подойти к этой проблеме - написать более конкретную черту типа. Например, это проверяет, что типы аргументов конвертируются и работают на все, что можно назвать.

#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 
} 
Смежные вопросы