How to detect the presence of a static member func

2019-01-25 22:57发布

问题:

I found several questions & answers on SO dealing with detecting at compile time (via SFINAE) whether a given class has a member of certain name, type, or signature. However, I couldn't find one that also applies to static public member functions (when pointer-to-member tricks won't work). Any ideas?

回答1:

Following may help: (https://ideone.com/nDlFUE)

#include <cstdint>

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static std::uint8_t check(helper<signature, &funcName>*);           \
        template<typename T> static std::uint16_t check(...);               \
    public:                                                                 \
        static                                                              \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
    }

Then define a traits:

DEFINE_HAS_SIGNATURE(has_foo, T::foo, void (*)(void));


回答2:

Here's one way:

#include <type_traits>

template<typename, typename>
struct has_fun;

template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {
    template<typename U, U> struct Check;

    template<typename U>
    static std::true_type Test(Check<Ret(*)(Args...), &U::fun>*);

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

    static const bool value = decltype(Test<T>(0))::value;
};

It's written for a function called fun. Use it like has_fun<T, int(int, int)>::value.

Here's another:

#include <type_traits>

template<typename, typename>
struct has_fun;

template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {

    struct No {}; // need a unique type for the second overload
                  // so it doesn't match Ret and give a false positive
    template<typename U>
    static auto Test(int) -> decltype( U::fun(std::declval<Args>()...) );

    template<typename U>
    static No Test(...);

    static const bool value =
        std::is_same<decltype(Test<U>(0)), Ret>{};
};

It might be sensible to test whether the return type of the function is convertible to Ret instead of checking for exatch match. Use is_convertible instead of is_same in that case and, at the same time, check that return type is different than No (as Yakk points out, there are types out there that can be constructed from just about anything).



回答3:

Simply invoke the member function and discard the result in a SFINAE context. If it succeeds, the method exists. If it fails, the method does not.

// not needed in C++1y
template<class T,class V=void>using enable_if_t=typename enable_if<T,V>::type;

// If the other tests fail, the type T does not have a method `foo` of
// signature Sig.  The class=void parameter is an implementation detail
// that in an industrial quality implementation we would hide in a helper
// template type.
template<class T,class Sig,class=void>struct has_foo:std::false_type{};

// For R(Args...), we attempt to invoke `T::foo` with (Args...), then check
// if we can assign the return value to a variable of type R.
template<class T,class R,class...Args>
struct has_foo<T,R(Args...),
  enable_if_t< // std:: in C++1y
    std::is_convertible<
      decltype( T::foo( std::declval<Args>()... ) ),
      R
    >::value
    && !std::is_same<R, void>::value
  >
>: std::true_type {};
// for `void` return value, we only care if the function can be invoked,
// no convertible test required:
template<class T,class...Args>
struct has_foo<T,void(Args...),
  decltype( void(T::foo( std::declval<Args>()... ) ) )
>: std::true_type {};

use:

 has_foo< bar, int(int) >::value

which checks if int r = T::foo( 7 ) is a valid expression, and not for exact signature match.