Вы используете свой шаблон как:
template<typename T1, typename T2>
constexpr bool CanApply = ValidCall<T1, T2>::value;
Но основной объявлен как:
template <class Void, class... T>
struct ValidCall;
Этот первый параметр шаблона называется Void
, потому что он имеет , чтобы быть void
- это то, с чем сопоставляется специализация. Таким образом, вы должны сделать это следующим образом:
template<typename T1, typename T2>
constexpr bool CanApply = ValidCall<void, T1, T2>::value;
// ^~~~
Но также это C ++ 17, и у нас есть для этого черта типа:
template<typename T1, typename T2>
constexpr bool CanApply = std::is_invocable_v<ApplyInvoker, T1, T2>;
Проблема в том, что ни Apply
, ни ApplyInvoker
на самом деле не работают с чертами типа. Apply
объявляет себя вызываемым с любыми двумя аргументами - иначе обнаружить невозможно. ApplyInvoker
объявляет себя вызываемым с любым количеством аргументов - но таким образом, что это приведет к серьезной ошибке компиляции, если вы попытаетесь выяснить с неправильным набором из них. Оба этих типа мы бы назвали SFINAE недружественными - они не дружат с чертами типов, они не тестируемы.
Если вы хотите, чтобы они действительно были тестируемыми, вам нужно переписать оба типа следующим образом :
struct Apply
{
template<typename T1, typename T2>
float apply(const T1& a, const T2& b) const
-> decltype(float(a / b))
{
return a / b;
}
};
struct ApplyInvoker
{
Apply a;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype(a.apply(std::forward<Args>(args)...)
{
return a.apply(std::forward<Args>(args)...);
}
};
или что-то подобное.