C++0X provides a rich type trait for generic programming. However, there is no type trait for probing class members. It is difficult to implement this type trait without the help of the compiler. Here we modify the requirements appropriately: The probe class has a member of the specified name and type.
In C + +, function overloading is the most common method of implementing type trait. However, the function overload is based on a type. Both the default parameters and access permissions are performed after the function overload. Here we want to detect if the specified member exists, so we need to find a way to convert the member to a type. Fortunately, templates support non typed parameters. The following shows an implementation based on this idea:
Namespace Van {
Namespace Type_traits {
Namespace Detail {
typedef char Small;
struct Big {char dummy[2];
Template<typename Type,type ptr>
struct Memberhelperclass;
Template<typename T,typename type>
Small memberhelper_f (memberhelperclass<type,&t::f> *);
Template<typename T,typename type>
Big Memberhelper_f (...);
}
Template<typename T,typename type>
struct HAS_MEMBER_F
{
enum {value=sizeof (detail::memberhelper_f<t,type> (0)) ==sizeof (Detail::small)};
};
}
}
struct A
{
static void f ();
};
struct B
{
};
#include <iostream>
using namespace Std;
int main ()
{
cout<<boolalpha;
Cout<<van::type_traits::has_member_f<a,void (*) () >::value<<endl;
Cout<<van::type_traits::has_member_f<b,void (*) () >::value<<endl;
}
If the Member "F" does not exist, then the conversion of the address "&t::f" to the type "Memberhelperclass" is invalid, so an overloaded version that accepts indefinite parameters is selected. Otherwise, a version that accepts "Memberhelperclass" will be selected because the version that accepts the indefinite parameter has the lowest priority in the overloaded resolution. Then Has_member_f can judge whether the member "F" exists by checking the return value of the Memberhelper_f function selected by the overloaded resolution. The code above supports both static and non-static members. It also supports both member functions and member variables. However, the above method has a flaw. If the probed member is not public, a compilation error is caused. This is because the access checks are performed after the overload resolution.
Because the member name itself cannot be used as a template parameter, we must explicitly add it to the class name of our helper class in order to differentiate it. To avoid duplication of effort, we can use macros to write generic versions of the following:
#define DEFINEHASMEMBER(Name)\
namespace van {\
namespace type_traits {\
namespace detail {\
template<typename T,typename Type>\
Small MemberHelper_##Name(MemberHelperClass<Type,&T::Name> *);\
template<typename T,typename Type>\
Big MemberHelper_##Name(...);\
}\
\
template<typename T,typename Type>\
struct has_member_##Name\
{\
enum {value=sizeof(detail::MemberHelper_##Name<T,Type>(0))==sizeof(detail::Small)};\
};\
}\
}