编译时间存在性检查成员函数,其签名适合可变参数包

Compile time existence checking for a member function with signature fit to variadic parameters pack

我想检查是否存在签名适合参数包的成员函数。我从已知的 SFINAE 概念开始,同时尝试扩展它以考虑参数包。但是此时我发现我不知道该怎么做。

我试着做这样的事情:

// Note: T object may have several functions with the name foo, but with different signatures.
//       a function foo also can be a template one with variadic parameters pack.
       
template<typename T, typename...Args>
class HAS_FUNCTION_FOO
{
    template<typename U>
    static bool test(decltype(&U::foo));

    template<typename U>
    static float test(...);

public:
    static constexpr bool value = std::is_integral<decltype(test<T>(Args...))>::value;

    //-------------------------------------------------------------^^^^^^^^^
    // how to do it? 

};

我想用它在编译时声明特定的对象——像这样:

class Bar
{
public:
    template<typename T, typename...Args>
    void doSomthing(T* p, Args&&...parameters)
    {
        // get correct object type:
        // if T has a function called foo with parameters fits for pack, then declare A, o.w declare B.
        using ObjType = typename std::conditional<HAS_FUNCTION_FOO<T, Args>::value, A, B>::type;

        // compute
        ObjType::doSomthing(p, std::forward<Args>(parameters)...);
    }

private:
    struct A
    {
        template<typename T, typename...Args>
        static void doSomthing(T* p, Args&&...parameters)
        {
            p->foo(std::forward<Args>(parameters)...);
        }
    };

    struct B
    {
        template<typename T, typename...Args>
        static void doSomthing(T* p, Args&&...parameters)
        {
            // do something else
        }
    };

};

可能是这样的:

template<typename T, typename...Args>
class HAS_FUNCTION_FOO
{
    template <typename U>
    static std::true_type test(
        typename std::enable_if<sizeof(
            decltype(std::declval<U>().foo(std::declval<Args>()...))*) != 0>::type*
    );

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

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

Demo