, но, к сожалению, static_assert запускается для init (поскольку специализация, вероятно, будет оценена позднее, так как объект создается в main ())
Не совсем.
Проблема в том, что init()
- это шаблонный метод, поэтому при написании
decltype(testSignature(&MemberPointerType::FUNCTION))
указатель не выбирается, поскольку компилятор не может выбрать правильный метод.
Вы можете попробовать с
decltype(testSignature(&MemberPointerType::template FUNCTION<__VA_ARGS__>))
, но теперь не работает для exec()
, который не является методом шаблона
Для работы как с шаблоном, так и без шаблона ... не простое прохождение через макрос variadi c, потому что часть variadi c не может быть пустой ... но я предлагаю что-то следующее
template <typename...>
struct wrap
{ };
#define CHECK4_MEMBER_FUNC(RETTYPE,FUN,...) \
template <class ClassType> \
class CIfCheck_##FUN \
{\
private: \
template <typename MPT> \
static auto testSig (wrap<void>) \
-> std::is_same<decltype(std::declval<MPT>().FUN()),\
RETTYPE>; \
\
template <typename MPT, typename ... As> \
static auto testSig (wrap<As...>) \
-> std::is_same<decltype(std::declval<MPT>().FUN(std::declval<As>()...)), \
RETTYPE>; \
\
template <typename...> \
static std::false_type testSig (...);\
\
public: \
using type = decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));\
static const bool value = type::value; \
};
Заметьте, что я добавил структуру wrap
обернуть типы параметров шаблона; обычно используется std::tuple
, но в этом случае нам нужно wrap<void>
, потому что std::tuple<void>
выдает ошибку.
Обратите внимание, что мое решение отличается от другой точки зрения (и может быть лучше или хуже, согласно вашим конкретным c потребностям): ваше решение проверяет наличие метода с точной подписью; В моем решении проверьте, есть ли метод, который можно вызвать с заданным списком аргументов.
Конкретный пример: предположим, что существует метод Bla::foo()
, который принимает значение long
void foo (long)
{ }
В вашем решении, если вы проверите параметр int
CHECK4_MEMBER_FUNC(void, foo, int);
static_assert( false == CIfCheck_foo<Bla>::value, "no foo with int");
, вы получите значение false
из CIfCheck_foo
, поскольку в Bla
нет метода foo
введите void(&BLA::*)(int)
(есть void(&BLA::*)(long)
, который отличается).
С моим методом вы получите значение true
от CIfCheck_foo
, потому что foo(long)
принимает также int
значение (и возвращаемый тип void
).
std :: false_type testExistence (...);
Почему именно я должен передать аргумент здесь? Если я удалю опцию variadi c аргумент ...
(и nullptr
и nullptr_t
), ошибки компилятора из-за неоднозначного существования testExistence()
.
То testExistence()
, как
template <typename...> \
static std::false_type testSig (...);\
- второй выбор.
Я имею в виду ... когда вы вызываете макрос testExistence()
внутри decltype()
decltype(testExistence<ClassType>(nullptr));
или мой вызов макроса testSig()
внутри decltype()
decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));
вызов, функционирующий с аргументом (nullptr
или wrap<__VA_ARGS__>{}
).
Когда доступен первый выбор (когда присутствует RETTYPE (MemberPointerType::*)(__VA_ARGS__)
в вашем случае, когда в моем примере вызывается метод с необходимыми аргументами), компилятор выбирает эту версию и возвращает std::true_type
(или std::is_same
в моем коде).
Но когда первый выбор недоступен?
Требуются вторые варианты, версии, возвращающие std::false
. Но звонок с аргументом. Многоточие здесь - это старый список аргументов C в стиле variadi c и допускается ноль или более аргументов, поэтому принимайте также один аргумент.
Если вы удалите многоточие (...
), второй выбор больше не может принимать аргумент (становится функцией с нулевым аргументом), и вы получаете ошибку компиляции, потому что компилятор не находит функцию второго выбора, совместимую с аргументом.