2017-07-15 98 views
5

我使用定义的is_callable结构如下检查功能是可调用

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

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

    static constexpr bool value = decltype(test<F>(nullptr))::value; 
}; 

我用这来测试拉姆达声明:

template <typename T> 
struct runner { 
    T t; 

    template <typename F, typename = typename std::enable_if<is_callable<F, T&>::value || is_callable<F, T&&>::value>::type> 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

runner<int> a{0}; 
a.run([&] (auto& x) { 
    x++; 
}); 

为什么这个失败的编译AppleClang上的enable_ifauto是否应该被正确推断?

+0

[Works for me](http://coliru.stacked-crooked.com/a/b83c21fddc4e15cb)。你的'clang'版本是什么,具体的错误信息是什么? – Rakete1111

+0

@ Rakete1111'候选模板被忽略:被'enable_if''禁用 Apple LLVM版本8.1.0(铛-802.0.42) – subzero

+0

你为什么要检查它是否可以用'T&'*或*'T &&'调用?你用'T&'来调用它,只需检查一下。 – Barry

回答

1

您的情况为true_typetest看起来不对。无论如何,你的代码比需要的更复杂。请尝试以下最小工作示例:

#include <utility> 

template<typename F, typename...Args> struct is_callable { 
    template<typename F2, typename...Args2> static constexpr std::true_type 
    test(decltype(std::declval<F2>()(std::declval<Args2>()...)) *) { return {}; } 

    template<typename F2, typename...Args2> static constexpr std::false_type 
    test(...) { return {}; } 

    static constexpr bool value = decltype(test<F, Args...>(nullptr))::value; 
}; 

void f0(); 
static_assert(is_callable<decltype(f0)>::value, "f0()"); 
static_assert(!is_callable<decltype(f0), int>::value, "f0(0)"); 

int f1(int); 
static_assert(!is_callable<decltype(f1)>::value, "f1()"); 
static_assert(is_callable<decltype(f1), int>::value, "f1(0)"); 
static_assert(!is_callable<decltype(f1), int, int>::value, "f1(0, 0)"); 

auto __attribute__((unused)) f2 = [](int, char *) { return 7; }; 
static_assert(is_callable<decltype(f2), int, char *>::value, "f2(int, char *)"); 
static_assert(!is_callable<decltype(f2), int, int>::value, "f2(int, int)"); 
+1

这是一个*可怕*'is_callable'。 –

+0

什么是可怕的?它可以与C++ 11,14和17一起工作,并且非常明确和清晰地使用SFINAE。 – user3188445

0

问题不在于is_callable测试类,而是您的使用情况。

当在功能的模板参数列表使用std::enable_if,你必须使用这种方式:

template <typename T> 
struct runner { 
    T t; 

    template 
    < 
    typename F, 
    std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value>* = nullptr 
    > 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

你试图使用的形式是用作尾随返回类型:

template<typename F> 
    auto run(F&& f) 
    -> std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value> 
    { 
    return f(t); 
    } 

is_callable类型是正确的。它看起来像我之前发布的堆栈溢出。

+1

你为什么认为你*有*以这种方式使用它? – Barry

+0

@Barry,因为它是启用函数的3种正确方法之一:type * = nullptr,尾随类型或假参数。 –

+0

我不确定你为什么这么认为。 'template <...,typename = std :: enable_if_t <...>>'是相当常用的。 – Barry