Si potrebbe aggiungere un ripiego in caso &E::foo
non è riuscito utilizzando SFINAE (e un altro nel caso in cui E::foo
non esiste affatto):
template <typename T>
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int);
template <typename T>
decltype(T::foo, std::true_type{}) is_member_foo(long);
template <typename T>
std::false_type is_member_foo(...);
template <typename T>
using IsMemberFoo = decltype(is_member_foo<T>(0));
static_assert(IsMemberFoo<A>{}, "No");
static_assert(IsMemberFoo<B>{}, "No");
static_assert(!IsMemberFoo<C>{}, "No");
static_assert(!IsMemberFoo<D>{}, "No");
static_assert(IsMemberFoo<E>{}, "No");
static_assert(!IsMemberFoo<F>{}, "No");
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { };
cosa fa questo codice:
- Se
&T::foo
è valido, controllerà se il membro è statico o non usa std::is_member_pointer
(la tua versione).
- Se
&T::foo
non è valida, spetta alla seconda sovraccarico (qui si è certi che foo
non è statica, o il primo di sovraccarico sarebbe stato scelto):
- Se
T::foo
è valido (esiste un membro), restituisce std::true_type
.
- Otherwize si ripresenta all'ultimo sovraccarico e restituisce
std::false_type
.
noti inoltre (grazie a @iammilind) che per private
membro, T::foo
non è valido , quindi il terzo sovraccarico sarà scelto.
esempio Lavorando su Ideone: http://ideone.com/FILHbK
note a margine (spiegazione estesa):
- Quando
&T::foo
è valido, i due primi sovraccarichi sono validi, ma il primo è scelto dal int
è un esatto partita mentre long
non lo è.
decltype(T::foo, std::true_type{})
: T::foo
: è qui solo per "lasciare che SFINAE" ricada al terzo sovraccarico se T::foo
non è valido, ma il tipo risultante è std::true_type
grazie all'operatore virgola.
Se si desidera, è possibile anche creare una versione generica (http://ideone.com/lzH2FB):
#define IsMember(MEM) \
template <typename T> \
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \
template<typename T> \
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \
template <typename T> \
std::false_type is_member_##MEM(...); \
template <typename T> \
using IsMember_##MEM = decltype(is_member_##MEM<T>(0));
// Instanciate IsMember_foo
IsMember(foo);
// Use it:
static_assert(IsMember_foo<A>{}, "No");
vedere questi due risposte, se si vuole incapsulare tutto in una classe anche (senza dover is_member_
funzioni) :
Correlato: http://stackoverflow.com/questions/8336274/pointer-to-member-that-is-a-reference-llegal – PiotrNycz