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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With