Мне было интересно, возможно ли расширить подход SFINAE, чтобы определить, имеет ли класс определенную функцию-член (как обсуждено здесь:
"Есть ли в C ++ метод, позволяющий узнать, имеет ли класс функцию-член с заданной сигнатурой?"
Проверить, имеет ли класс функцию-член с заданной сигнатурой
) для поддержки шаблонных функций-членов? Например. чтобы можно было обнаружить функцию foo в следующем классе:
struct some_class {
template < int _n > void foo() { }
};
Я подумал, что это возможно сделать для конкретного экземпляра foo (например, проверьте, является ли void foo< 5 >()
член) следующим образом:
template < typename _class, int _n >
class foo_int_checker {
template < typename _t, void (_t::*)() >
struct sfinae { };
template < typename _t >
static big
test( sfinae< _t, &_t::foo< _n > > * );
template < typename _t >
static small
test( ... );
public:
enum { value = sizeof( test< _class >( 0 ) ) == sizeof( big ) };
};
Затем выполните foo_int_checker< some_class, 5 >::value
, чтобы проверить, есть ли в some_class
элемент void foo< 5 >()
. Однако в MSVC ++ 2008 это всегда возвращает false
, тогда как g ++ выдает следующие синтаксические ошибки в строке test( sfinae< _t, &_t::foo< _n > > );
test.cpp:24: error: missing `>' to terminate the template argument list
test.cpp:24: error: template argument 2 is invalid
test.cpp:24: error: expected unqualified-id before '<' token
test.cpp:24: error: expected `,' or `...' before '<' token
test.cpp:24: error: ISO C++ forbids declaration of `parameter' with no type
Кажется, что оба сбоя, потому что я пытаюсь получить адрес экземпляра функции шаблона от типа, который сам является параметром шаблона. Кто-нибудь знает, возможно ли это или по каким-то причинам запрещено стандартом?
EDIT: похоже, я пропустил синтаксис ::template
, чтобы g ++ правильно скомпилировал приведенный выше код. Если я изменяю бит, в котором получаю адрес функции, на &_t::template foo< _n >
, то программа компилируется, но я получаю то же поведение, что и MSVC ++ (value
всегда устанавливается на false
).
Если я закомментирую перегрузку ...
test
, чтобы заставить компилятор выбрать другой, я получу следующую ошибку компилятора в g ++:
test.cpp: In instantiation of `foo_int_checker<A, 5>':
test.cpp:40: instantiated from here
test.cpp:32: error: invalid use of undefined type `class foo_int_checker<A, 5>'
test.cpp:17: error: declaration of `class foo_int_checker<A, 5>'
test.cpp:32: error: enumerator value for `value' not integer constant
, где строка 32 - это строка enum { value = sizeof( test< _class >( 0 ) ) == sizeof( big ) };
. К сожалению, это не помогает мне диагностировать проблему :(. MSVC ++ выдает похожую неописуемую ошибку:
error C2770: invalid explicit template argument(s) for 'clarity::meta::big checker<_checked_type>::test(checker<_checked_type>::sfinae<_t,&_t::template foo<5>> *)'
на той же строке.
Странно то, что если я получаю адрес из определенного класса, а не параметра шаблона (т.е. вместо &_t::template foo< _n >
я делаю &some_class::template foo< _n >
), тогда я получаю правильный результат, но тогда мой класс проверки ограничивается проверкой один класс (some_class
) для функции. Также, если я сделаю следующее:
template < typename _t, void (_t::*_f)() >
void
f0() { }
template < typename _t >
void
f1() {
f0< _t, &_t::template foo< 5 > >();
}
и позвоните f1< some_class >()
, тогда я не получу ошибку компиляции на &_t::template foo< 5 >
. Это говорит о том, что проблема возникает только при получении адреса шаблонной функции-члена от типа, который сам является параметром шаблона в контексте SFINAE. Argh!