How to check with SFINAE if a member exists, witho

2019-02-18 06:27发布

问题:

In pre-C++11 code, if I'm looking for a member variable whose type I don't know, how can I use SFINAE to check if the member exists?

回答1:

Here's an example using Member detector idiom that you asked for:

template<typename T>
struct has_x {
    typedef char(&yes)[1];
    typedef char(&no)[2];

    // this creates an ambiguous &Derived::x if T has got member x

    struct Fallback { char x; };
    struct Derived : T, Fallback { };

    template<typename U, U>
    struct Check;

    template<typename U>
    static no test(Check<char Fallback::*, &U::x>*);

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

    static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
};

#include <iostream>

struct A { private:  int x; };   // works with private, too
struct B { const char x; };
struct C { void x() volatile ; };
struct D : A { };
struct E {};
struct F : A, B {};  // note that &F::x is ambiguous, but
                     // the test with has_x will still succeed

int main()
{
    std::cout
        << has_x<A>::value                // 1
        << has_x<const B>::value          // 1
        << has_x<volatile C>::value       // 1
        << has_x<const volatile D>::value // 1
        << has_x<E>::value                // 0
        << has_x<F>::value;               // 1
}

Live test.

It should work with MSVC, too.



回答2:

Here's a solution that I've checked on newer versions of GCC and Clang, as well as on both newer and older versions of Visual C++:

#if defined(_MSC_VER) && _MSC_VER < 1400
#define GEN_MEMBER_CHECKER(Prefix, Member)                                                                  \
    template<class T> struct Prefix##Member                                                             \
    { enum { __if_not_exists(T::Member) { value = 0 } __if_exists(T::Member) { value = 1 } }; };
#else
#define GEN_MEMBER_CHECKER(Prefix, Member)                                                                  \
    template<class T> struct Prefix##Member;                                                            \
    template<class T> struct Prefix##Member<T const> : Prefix##Member<T> { };                           \
    template<class T> struct Prefix##Member<T volatile> : Prefix##Member<T> { };                        \
    template<class T> struct Prefix##Member<T volatile const> : Prefix##Member<T> { };                  \
    template<class T> struct Prefix##Member<T &> : Prefix##Member<T> { };                               \
    template<class T> struct Prefix##Member<T *> { enum { value = 0 }; };                               \
    template<> struct Prefix##Member<void>                                                              \
    {                                                                                                   \
    private:                                                                                            \
        template<class T> static unsigned char (&test(int, T const &))[1U + 1U];                    \
        static unsigned char (&test(int, ...))[1U];                                                 \
    public:                                                                                             \
        template<class T>                                                                           \
        static unsigned char (&check(int, Prefix##Member<T> *))[1U + sizeof(test(0, &T::Member))];  \
        static unsigned char (&check(int, ...))[1U];                                                \
    };                                                                                                  \
    template<class T> struct Prefix##Member                                                             \
    { enum { value = sizeof(Prefix##Member<void>::check(0, (Prefix##Member *)0)) > 2U }; }
#endif

Example:

#include <stdio.h>

GEN_MEMBER_CHECKER(member_check_, Member);

struct HasMember { int Member; };
struct DoesntHaveMember { };

int main()
{
    printf("%u %u\n",
        member_check_Member<HasMember>::value,
        member_check_Member<DoesntHaveMember>::value);
}

Output:

1 0