I want to write a type trait to check if some type has a member member. If member were public, there are any number of ways to do this (e.
You can create another class MemberBase that does have that member, and then subclass the two classes (the class to check T and BaseMember) and try to access the member of the subclass. If T also has a member member, then you will get an ambiguity problem.
Code:
#include <type_traits>
// Yakk's can_apply
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;
// Main code
class MemberBase {
public:
int member;
};
template<class ToCheck>
class MemberCheck: public ToCheck, public MemberBase {
};
template <typename T>
using member_type = decltype(&T::member);
template <typename T>
using hasnot_member = can_apply<member_type, MemberCheck<T>>;
template <typename T>
using static_not = std::integral_constant<bool, !T::value>;
template <typename T>
using has_member = static_not<hasnot_member<T>>;
// Tests
class A {
int member;
};
class Ap {
public:
int member;
};
class B {
float member;
};
class C {
int member();
};
class D {
};
static_assert(has_member<A>{}, "!"); // ok
static_assert(has_member<Ap>{}, "!"); // ok
static_assert(has_member<B>{}, "!"); // ok
static_assert(has_member<C>{}, "!"); // ok
static_assert(has_member<D>{}, "!"); // fail
However, this definitely smells like a dirty hack to me.
There is indeed a way for non-final non-union class types:
namespace detail {
struct P {typedef int member;};
template <typename U>
struct test_for_member : U, P
{
template <typename T=test_for_member, typename = typename T::member>
static std::false_type test(int);
static std::true_type test(float);
};
}
template <typename T>
using test_for_member =
std::integral_constant<bool, decltype(detail::test_for_member<T>::test(0)){}>;
Demo. The trick is to check whether lookup into different base classes will yield an ambiguity. [class.member.lookup]/2:
Member name lookup determines the meaning of a name (id-expression) in a class scope (3.3.7). Name lookup can result in an ambiguity, in which case the program is ill-formed. […] Name lookup takes place before access control (3.4, Clause 11).
Note that GCCs lookup is broken insofar as it ignores non-type names for lookup in typename-specifiers.