消除函数指针和指向 class 实例的指针之间的歧义

disambiguate between function pointer and pointer to class instance

我有一个实用程序,它执行 static_assert 是否可以使用给定的参数列表调用函数

我有 2 个重载:

对于 lambda 和自由函数:

template<typename Func, typename... Args>
void assert_is_callable(Func&&, Args&&...)

成员函数:

template<typename Class, typename MemFunc, typename... Args>
void assert_is_callable(Class*, MemFunc, Args&&...)

我遇到的问题是,当我用自由函数调用 assert_is_callable 时,选择了第二个重载;函数指针推导为 Class,第一个参数推导为 MemFunc,其余参数推导为 Args...

void func(int, double, char) {  }

assert_is_callable(func, 1, 2.3, 'c'); // selects the member_function version

编译错误输出:

In instantiation of 'void assert_is_callable(Class*, MemFunc, Args&& ...) [with Class = void(int, double, char); MemFunc = int; Args = {double, char}]':
prog.cpp:79:41:   required from here
prog.cpp:51:5: error: static assertion failed: the provided type is not callable with the supplied arguments
     static_assert(is_callable_member_function<Class, MemFunc, Args...>::value,
     ^

问题:

如何防止在将 assert_is_callable 与自由函数一起使用时选择成员函数重载?

示例:

working example on ideone

#include <tuple>
#include <type_traits>
#include <functional>

// functor

template<class Func, class... Args>
class is_callable
{
    using yes = char(&)[2];
    using no  = char(&)[1];

    template<class F>
    static yes check(decltype(std::declval<F&>()(std::declval<Args>()...))*);

    template<class F>
    static no check(...);
public:
    enum { value = (sizeof(check<Func>(nullptr)) == sizeof(yes)) };
};

// member function

template<typename Class, typename MemFunc, class... Args>
class is_callable_member_function
{
    using yes = char(&)[2];
    using no  = char(&)[1];

    template<class C, class MF>
    static yes check(decltype((std::declval<C>().*std::declval<MF>())(std::declval<Args>()...))*);

    template<class C, class MF>
    static no check(...);
public:
    enum { value = (sizeof(check<Class, MemFunc>(nullptr)) == sizeof(yes)) };
};

//////////////////////////////////

template<typename Func, typename... Args>
void assert_is_callable(Func&&, Args&&...)
{
    static_assert(is_callable<Func, Args...>::value,
            "the provided type is not callable with the supplied arguments");
}

template<typename Class, typename MemFunc, typename... Args>
void assert_is_callable(Class*, MemFunc, Args&&...)
{
    static_assert(is_callable_member_function<Class, MemFunc, Args...>::value,
            "the provided type is not callable with the supplied arguments");
}

//////////////////////////////////

struct Foo
{
    void func(int, double, char)
    {
    }
};

void func(int, double, char)
{
}

int main()
{
    // member function
    Foo f;
    assert_is_callable(&f, &Foo::func, 1, 2.3, 'c');

    // lambda
    auto lambda = [](int, double, char) { };
    assert_is_callable(lambda, 1, 2.3, 'c');

    // free function
//    assert_is_callable(func, 1, 2.3, 'c'); // selects the member_function version

    return 0;
}

可以使用SFINAE,也可以直接使用强类型:

template<typename Class, typename C, typename MemFunc, typename... Args>
std::enable_if_t<std::is_class<Class>::value>
assert_is_callable(Class*, MemFunc (C::*), Args&&...)
{
    static_assert(is_callable_member_function<Class, MemFunc (C::*), Args...>::value,
            "the provided type is not callable with the supplied arguments");
}

Demo