РЕДАКТИРОВАТЬ : завершено и исправлено.
Другой подход, использующий неоднозначность от наследования, вероятно, функционально эквивалентен вашему подходу 2. Я помню, что у меня были проблемы с подходом 1 (хотя он компилируется с G ++ 4.4.5), потому что разрешение имени вызвало ошибку, а не ошибку замены. Мне пришлось прибегнуть к:
template <class T>
struct has_foo
{
struct fallback { void foo(...); };
struct D : T, fallback { };
template <typename U, U> struct K;
// Will be ambiguous for U = D iff T has a foo member function.
// It doesn't trigger an error, since D will always have at least one
// foo member function.
template <class U> static char (&test(K<void (fallback::*)(...), &U::foo>*))[1];
template <class U> static char (&test(...))[2];
static const bool value = sizeof(test<D>(0)) == 2;
};
Это работает, когда T является классом, поэтому вы можете добавить свой слой для проверки, является ли T типом класса.
Обратите внимание, что будет обнаружена любая функция-член foo
. Если вы хотите проверить, может ли обнаруженная функция foo
вызываться с заданными аргументами, вы должны сделать еще один слой SFINAE:
// Check whether foo can be called with an argument of type Arg
// and yields an element of type Res.
// If you need Res = void, this code does not work.
template <class T, typename Arg, typename Res>
struct check_foo
{
struct flag {};
struct D : T { using T::foo; flag foo(...); };
template <typename U>
static char (&test(U))[1];
template <typename> static char (&test(...))[2];
static Arg f();
static const bool value = sizeof(test<Arg>( ((D*)0)->foo(f()) )) == 1;
};