Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting presence of an arbitrary member

It is easy to write a template that will detect the presence of a specific member within a type using void_t:

#include <type_traits>

// This comes from cppreference
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

// primary template handles types that have no ::aMember 
template< class T, class = void_t<> >
struct has_aMember : std::false_type { };

// specialization recognizes types that do have a ::aMember
template< class T >
struct has_aMember<T, void_t<decltype( T::aMember )>> : std::true_type { };

Now, if I want to detect whether some other member is present, I would have to copy-paste the detector templates and just change aMember to otherMember:

template< class T, class = void_t<> >
struct has_otherMember : std::false_type { };

template< class T >
struct has_otherMember<T, void_t<decltype( T::otherMember )>> : std::true_type { };

I would like to avoid this copy-pasting and pass the member name as a parameter to a more generic version of detection template:

template< class T, class member, class = void_t<> >
struct has_arbitrary_member : std::false_type { };

template< class T, class member >
struct has_arbitrary_member<T, void_t<decltype( T::member )>> : std::true_type { };

so that I could use this has_arbitrary_member by passing the type and the member name to as template parameter:

 std::cout << has_arbitrary_member<MyType, aMember>();
 std::cout << has_arbitrary_member<MyType, otherMember>();

However, with the definition I drafted above, this will not compile. Is there some other way to implement such functionality?

like image 473
Rostislav Avatar asked Oct 13 '15 17:10

Rostislav


2 Answers

What you're trying to do isn't possible in general because you cannot suppress name lookup on aMember unless you modify the token stream using a macro. It follows that you can never get away with using a name that is not in scope, unless you are currently inside SFINAE, which will suppress the error.

This means you really do have to write a separate template for each name.

This is one of those cases where using macros is not a bad idea, although that depends on whether you really do need to check for members of a specific name, or whether there are better ways to accomplish your objective.

like image 153
Brian Bi Avatar answered Oct 18 '22 19:10

Brian Bi


No, you cannot pass a member name as a template parameter without mentioning what class/struct the member name comes from.

And if that class/struct doesn't have that member name, then the code has an error.

Now, void_t< decltype( &MyType::some_member ) > is either void or a substitution failure depending on if some_member exists. Often that solves a SFINAE problem where you don't need a compile time bool.

If you want code to run if some type does not have a member, can do this with careful choice of overload order, or template specializations, etc.

It is not as powerful as has_arbitrary_member<MyType, some_member>.

template<class T>
using some_member_type = decltype( std::declval<T>().some_member );

is a simple trait that, with std::experimental::is_detected you can do:

is_detected_v< some_member_type, T >

or

template<class T>
constexpr bool has_some_member_v = is_detected_v< some_member_type, T >;

which behaves like has_arbitrary_member<MyType, some_member>. I find that is best in practice.

like image 3
Yakk - Adam Nevraumont Avatar answered Oct 18 '22 20:10

Yakk - Adam Nevraumont