Вы можете добавить запасной вариант в случае, если &E::foo
терпит неудачу с использованием SFINAE (и еще один в случае E::foo
не существует вообще):
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 { };
Что делает этот код:
- Если
&T::foo
является valid, он проверяет, является ли элемент статическим или не использует std::is_member_pointer
(ваша версия).
- Если
&T::foo
не является действительным, он возвращается ко второй перегрузке (здесь вы уверены, что foo
не является статичным, или первая перегрузка была бы выбрана):
- Если
T::foo
правомочно (член существует), он возвращает std::true_type
.
- Otherwize он возвращается к последней перегрузке и возвращает
std::false_type
.
Также обратите внимание (благодаря @iammilind), что для private
члена, T::foo
является не действует, поэтому третья перегрузка будет выбран.
Рабочий пример на ideone: http://ideone.com/FILHbK
сторона отмечает (расширенное объяснение):
- Когда
&T::foo
действительно, две первые перегрузки справедливы, но первый один выбирается так int
является точным в то время как long
нет.
decltype(T::foo, std::true_type{})
: T::foo
только здесь, чтобы «позволить SFINAE» вернуться к третьей перегрузке, если T::foo
недействителен, но полученный результат std::true_type
благодаря оператору запятой.
Если вы хотите, вы также можете создать версию родовое (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");
Также смотрите эти два ответа, если вы хотите, чтобы инкапсулировать все в классе (без is_member_
функций) :
Связанный: http://stackoverflow.com/questions/8336274/pointer-to-member-that-is-a-reference-illegal – PiotrNycz