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

时间:2016-01-08 17:30:02

标签: c++ sfinae typetraits

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

我有2次重载:

对于lambdas和自由函数

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

1 个答案:

答案 0 :(得分:2)

您可以使用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