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?
Asked
Active
Viewed 939 times
2 Answers
4
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
}
It should work with MSVC, too.

jrok
- 54,456
- 9
- 109
- 141
-
Wow, I'm so surprised this actually works! I thought it required you to know the type, and I also thought it requires a destructor in the base class as well. +1 to you! – user541686 Apr 13 '14 at 11:07
-
By the way, side note, but is `test(...)` legal? I wasn't sure if variadics needed at least 1 argument... – user541686 Apr 13 '14 at 11:14
-
Note: The test is true if you have a `struct G { typedef int x; };` – Apr 13 '14 at 11:40
-
@DieterLücking True. Further disambiguation may be needed if you need to be really sure what a name is. – jrok Apr 13 '14 at 11:45
-
@Mehrdad That's true in C, IIRC. – jrok Apr 13 '14 at 11:45
0
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

user541686
- 205,094
- 128
- 528
- 886
-
Can you explain the code a bit? What's the purpose of all the specializations (espcialy `
`)? Why the `const` and `const volatile` overloads of `test`? Note that you've got an ambiguous partial specialization when the type tested is `const volatile ([Coliru.](http://coliru.stacked-crooked.com/a/8c768c0d2fef8a23))`. I'm asking because it can be [much simpler](http://coliru.stacked-crooked.com/a/419465c2747cf2f4). (Notice test for `F` resulting in `0` in both ways) – jrok Apr 13 '14 at 07:53 -
@jrok: The `void` specialization is simply so I can put helpers inside the type, to prevent namespace pollution. The `const` and `volatile` specializations are there because I thought they made sense; you can take them out if you want. Thanks for the note regarding the ambiguity, I fixed it. And thanks for pointing out the `volatile` overload of `test`, I removed that too -- it was an artifact of my previous attempts and I didn't realize I could remove it. Note that your version doesn't work for Visual C++, whereas mine does (it doesn't require the `__if_exists` intrinsic). – user541686 Apr 13 '14 at 08:11
-
1I don't have access to MSVC. What it is that it has trouble with? One more thing - it doesn't work with private members, you get a compiler error. Consider switchign to [member detector idiom.](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector) – jrok Apr 13 '14 at 08:30
-
@jrok: It has trouble with the `sizeof`. Would you mind giving me a working example of how you think this is actually possible with the "member detector idiom" you linked to? (The code there doesn't do what the question is asking, as far as I can tell.) – user541686 Apr 13 '14 at 08:36