How to detect whether there is a specific member variable in class?
Here is a solution simpler than Johannes Schaub - litb's one. It requires C++11.
#include <type_traits>
template <typename T, typename = int>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
Update: A quick example and the explanation on how this works.
For these types:
struct A { int x; };
struct B { int y; };
we have HasX<A>::value == true
and HasX<B>::value == false
. Let's see why.
First recall that std::false_type
and std::true_type
have a static constexpr bool
member named value
which is set to false
and true
, respectively. Hence, the two templates HasX
above inherit this member. (The first template from std::false_type
and the second one from std::true_type
.)
Let's start simple and then proceed step by step until we get to the code above.
1) Starting point:
template <typename T, typename U>
struct HasX : std::false_type { };
In this case, there's no surprise: HasX
derives from std::false_type
and hence HasX<bool, double>::value == false
and HasX<bool, int>::value == false
.
2) Defaulting U
:
// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
Given that U
defaults to int
, Has<bool>
actually means HasX<bool, int>
and thus, HasX<bool>::value == HasX<bool, int>::value == false
.
3) Adding a specialization:
// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };
In general, thanks to the primary template, HasX<T, U>
derives from std::false_type
. However, there exists a specialization for U = int
which derives from std::true_type
. Therefore, HasX<bool, double>::value == false
but HasX<bool, int>::value == true
.
Thanks to the default for U
, HasX<bool>::value == HasX<bool, int>::value == true
.
4) decltype
and a fancy way to say int
:
A little digression here but, please, bear with me.
Basically (this is not entirely correct), decltype(expression)
yields the type of expression. For instance, 0
has type int
thus, decltype(0)
means int
. Analogously, 1.2
has type double
and thus, decltype(1.2)
means double
.
Consider a function with this declaration:
char func(foo, int);
where foo
is some class type. If f
is an object of type foo
, then decltype(func(f, 0))
means char
(the type returned by func(f, 0)
).
Now, the expression (1.2, 0)
uses the (built-in) comma operator which evaluates the two sub-expressions in order (that is, first 1.2
and then 0
), discards the first value and results in the second one. Hence,
int x = (1.2, 0);
is equivalent to
int x = 0;
Putting this together with decltype
gives that decltype(1.2, 0)
means int
. There's nothing really special about 1.2
or double
here. For instance, true
has type bool
and decltype(true, 0)
means int
as well.
What about a class type? For instace, what does decltype(f, 0)
mean? It's natural to expect that this still means int
but it might not be the case. Indeed, there might be an overload for the comma operator similar to the function func
above that takes a foo
and an int
and returns a char
. In this case, decltype(foo, 0)
is char
.
How can we avoid the use of a overload for the comma operator? Well, there's no way to overload the comma operator for a void
operand and we can cast anything to void
. Therefore, decltype((void) f, 0)
means int
. Indeed, (void) f
casts f
from foo
to void
which basically does nothing but saying that the expression must be considered as having type void
. Then the built-in operator comma is used and ((void) f, 0)
results in 0
which has type int
. Hence, decltype((void) f, 0)
means int
.
Is this cast really necessary? Well, if there's no overload for the comma operator taking foo
and int
then this isn't necessary. We can always inspect the source code to see if there's such operator or not. However, if this appear in a template and f
has type V
which is a template parameter, then it's no longer clear (or even impossible to know) whether such overload for the comma operator exists or not. To be generic we cast anyway.
Bottom line: decltype((void) f, 0)
is a fancy way to say int
.
5) SFINAE:
This is a whole science ;-) OK I'm exagerating but it's not very simple either. So I'll keep the explanation to the bare minimum.
SFINAE stands for Substitution Failure is Not An Error. It means that when a template parameter is substituted by a type, an illegal C++ code might appear but, in some circunstances, instead of aborting compilation the compiler simply ignores the offending code as if it wasn't there. Let's see how it applies to our case:
// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };
// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
Here, again, decltype((void) T::x, 0)
is a fancy way to say int
but with the benefit of SFINAE.
When T
is substituted with a type, an invalid construct might appear. For instance, bool::x
is not valid C++, so substituting T
with bool
in T::x
yields an invalid construct. Under the SFINAE principle, the compiler doesn't reject the code, it simply ignores (parts of) it. More precisely, as we have seenHasX<bool>
means actually HasX<bool, int>
. The specialization for U = int
should be selected but, while instantiating it, the compiler finds bool::x
and ignores the template specialization altogether as if it didn't exist.
At this point, the code is essencially the same as in case (2) above where just the primary template exists. Hence, HasX<bool, int>::value == false
.
The same argument used for bool
holds for B
since B::x
is an invalid construct (B
has no member x
). However, A::x
is OK and the compiler sees no issue in instantiating the specialization for U = int
(or, more precisely, for U = decltype((void) A::x, 0)
). Hence, HasX<A>::value == true
.
6) Unnaming U
:
Well, looking at the code in (5) again, we see that the name U
is not used anywhere but in its declaration (typename U
). We can then unname the second template argument and we obtain the code shown at the top of this post.
Another way is this one, which relies on SFINAE for expressions too. If the name lookup results in ambiguity, the compiler will reject the template
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A { int x; };
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
It's based on a brilliant idea of someone on usenet.
Note: HasX checks for any data or function member called x, with arbitrary type. The sole purpose of introducing the member name is to have a possible ambiguity for member-name lookup - the type of the member isn't important.
I got redirected here from a question which has been closed as a duplicate of this one. I know it's an old thread, but I just wanted to suggest an alternative (simpler?) implementation that works with C++11. Supposing we want to check whether a certain class has a member variable called id
:
#include <type_traits>
template<typename T, typename = void>
struct has_id : std::false_type { };
template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
That's it. And here is how it would be used (live example):
#include <iostream>
using namespace std;
struct X { int id; };
struct Y { int foo; };
int main()
{
cout << boolalpha;
cout << has_id<X>::value << endl;
cout << has_id<Y>::value << endl;
}
Things can be made even simpler with a couple of macros:
#define DEFINE_MEMBER_CHECKER(member) \
template<typename T, typename V = bool> \
struct has_ ## member : false_type { }; \
template<typename T> \
struct has_ ## member<T, \
typename enable_if< \
!is_same<decltype(declval<T>().member), void>::value, \
bool \
>::type \
> : true_type { };
#define HAS_MEMBER(C, member) \
has_ ## member<C>::value
Which could be used this way:
using namespace std;
struct X { int id; };
struct Y { int foo; };
DEFINE_MEMBER_CHECKER(foo)
int main()
{
cout << boolalpha;
cout << HAS_MEMBER(X, foo) << endl;
cout << HAS_MEMBER(Y, foo) << endl;
}
UPDATE: I've recently done some more with the code I posted in my original answer, so I'm updating this to account for changes/additions.
Here are some usage snippets: *The guts for all this are farther down
Check for member x
in a given class. Could be var, func, class, union, or enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
Check for member function void x()
:
//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
Check for member variable x
:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
Check for member class x
:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
Check for member union x
:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
Check for member enum x
:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
Check for any member function x
regardless of signature:
CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
OR
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
Details and core:
/*
- Multiple inheritance forces ambiguity of member names.
- SFINAE is used to make aliases to member names.
- Expression SFINAE is used in just one generic has_member that can accept
any alias we pass it.
*/
template <typename... Args> struct ambiguate : public Args... {};
template<typename A, typename = void>
struct got_type : std::false_type {};
template<typename A>
struct got_type<A> : std::true_type {
typedef A type;
};
template<typename T, T>
struct sig_check : std::true_type {};
template<typename Alias, typename AmbiguitySeed>
struct has_member {
template<typename C> static char ((&f(decltype(&C::value))))[1];
template<typename C> static char ((&f(...)))[2];
//Make sure the member name is consistently spelled the same.
static_assert(
(sizeof(f<AmbiguitySeed>(0)) == 1)
, "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
);
static bool const value = sizeof(f<Alias>(0)) == 2;
};
Macros (El Diablo!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member) \
\
template<typename T, typename = std::true_type> \
struct Alias_##member; \
\
template<typename T> \
struct Alias_##member < \
T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \
> { static const decltype(&T::member) value; }; \
\
struct AmbiguitySeed_##member { char member; }; \
\
template<typename T> \
struct has_member_##member { \
static const bool value \
= has_member< \
Alias_##member<ambiguate<T, AmbiguitySeed_##member>> \
, Alias_##member<AmbiguitySeed_##member> \
>::value \
; \
}
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_var_##var_name : std::false_type {}; \
\
template<typename T> \
struct has_member_var_##var_name< \
T \
, std::integral_constant< \
bool \
, !std::is_member_function_pointer<decltype(&T::var_name)>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \
\
template<typename T, typename = std::true_type> \
struct has_member_func_##templ_postfix : std::false_type {}; \
\
template<typename T> \
struct has_member_func_##templ_postfix< \
T, std::integral_constant< \
bool \
, sig_check<func_sig, &T::func_name>::value \
> \
> : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_class_##class_name : std::false_type {}; \
\
template<typename T> \
struct has_member_class_##class_name< \
T \
, std::integral_constant< \
bool \
, std::is_class< \
typename got_type<typename T::class_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_union_##union_name : std::false_type {}; \
\
template<typename T> \
struct has_member_union_##union_name< \
T \
, std::integral_constant< \
bool \
, std::is_union< \
typename got_type<typename T::union_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_enum_##enum_name : std::false_type {}; \
\
template<typename T> \
struct has_member_enum_##enum_name< \
T \
, std::integral_constant< \
bool \
, std::is_enum< \
typename got_type<typename T::enum_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func) \
template<typename T> \
struct has_member_func_##func { \
static const bool value \
= has_member_##func<T>::value \
&& !has_member_var_##func<T>::value \
&& !has_member_class_##func<T>::value \
&& !has_member_union_##func<T>::value \
&& !has_member_enum_##func<T>::value \
; \
}
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member) \
CREATE_MEMBER_CHECK(member); \
CREATE_MEMBER_VAR_CHECK(member); \
CREATE_MEMBER_CLASS_CHECK(member); \
CREATE_MEMBER_UNION_CHECK(member); \
CREATE_MEMBER_ENUM_CHECK(member); \
CREATE_MEMBER_FUNC_CHECK(member)