I have seen this question which allows one to check for the existence of a member function, but I’m trying to find out whether a class has a member type.
In the example below, both evaluate to “false”, but I would like to find a way so that has_bar<foo1>::value evaluates to false, and has_bar<foo2>::value evaluates to true.
Is this possible?
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T>
class has_bar
{
typedef char yes;
typedef long no;
template <typename C> static yes check( decltype(&C::bar) ) ;
template <typename C> static no check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};
int main()
{
std::cout << has_bar<foo1>::value << std::endl;
std::cout << has_bar<foo2>::value << std::endl;
return 0;
}
Edit: implementing a specialisation in response to the answers below:
…if you use C::bar in the target template, the template will be
discarded automatically for types that don’t have that nested type.
I have tried to do this, but am clearly missing something
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T, typename U = void>
struct target
{
target()
{
std::cout << "default target" << std::endl;
}
};
template<typename T>
struct target<T, typename T::bar>
{
target()
{
std::cout << "specialized target" << std::endl;
}
};
int main()
{
target<foo1>();
target<foo2>();
return 0;
}
You cannot obtain a pointer to member to a type member:
The subexpression
&C::barwill only be valid whenbaris a non-type member ofC. But what you need to check is whether it is a type. A minimal change to your template could be:If
baris a nested type ofC, then that function overload will be a valid candidate (the 0 will be a pointer to whateverC::bartype is), but ifCdoes not contain a nestedbarthen it will be discarded and the second test will be the only candidate.There is a different question as of whether the trait is needed at all, since if you use
C::barin the target template, the template will be discarded automatically for types that don’t have that nested type.EDIT
What I meant is that in your approach you need to create a trait for each and every possible nested type, just to generate a template that does or does not hold a nested type (
enable_if). Let’s take a different approach… First we define a general utility to select a type based on a condition, this is not required for this problem, and a simplertemplate <typename T> void_type { typedef void type; };would suffice, but the utility template can be useful in other cases:Now se just need to use SFINAE for class template specializations:
Note that the main difference with your approach is the use of an extra intermediate template (the one for which Substitution will Fail –and Is Not An Error) that yields a
voidtype (on success). This is the reason why thevoid_typetemplate above would also work: you just need to use the nested type as argument to a template, and have that fail, you don’t really care what the template does, as long as the evaluation is a nestedtype(that must bevoid) if it succeeds.In case it is not obvious (it wasn’t at first for me) why your approach doesn’t work, consider what the compiler needs to do when it encounters
target<foo2>: The first step is finding that there is a template calledtarget, but that template takes two arguments of which only one was provided. It then looks in the base template (the one that is not specialized) and finds that the second argument can be defaulted tovoid. From this point on, it will consider your instantiation to be:target<foo2,void>(after injecting the defaulted argument). And it will try to match the best specialization. Only specializations for which the second argument isvoidwill be considered. Your template above will only be able to use the specialized version ifT::barisvoid(you can test that by changingfoo2to:struct foo2 { typedef void bar; }. Because you don’t want the specialization to kick in only when the nested type isvoidyou need the extra template that will takeC::bar(and thus fail if the type does not contain a nestedbar) but will always yieldvoidas the nested type.