As commenters suggest (thanks @DanHulme), this is doable with SFINAE without any assumptions - similarly to the way it was done in
Find out whether a C++ object is callable
At least, under a certain assumption.
However, it's slightly tricky to get the code correct for a templated operator()
, because of C++ syntax ambiguities. I've also added in some comments to briefly explain how the SFINAE works here, in favor of the less-C++-savvy readers (of the code, not of this question).
Anyway, here's the adaptation:
#include <type_traits>
namespace detail {
template <class>
struct sfinae_true : std::true_type{};
template <class F, typename T>
static auto test_invoke_operator(int ) ->
sfinae_true<decltype(std::declval<F>().template operator()<T>())>;
template <class, typename>
static auto test_invoke_operator(long) -> std::false_type;
} // namespace detail
template <class F, typename T>
struct has_invoke_operator : decltype( detail::test_invoke_operator<F,T>(int{}) )
{ };
// The overload resolution here will depend on the test for invoke_operator:
// * If F has an operator()<T>, the `int` variant substitution succeeds,
// so has_invoke_operator inherits true_type.
// * If F does not have an operator()<T>, the `int` variant substitution fails,
// but we still get a matching candidate - the `long` variant, which
// means has_invoke_operator inherits false_type
Working example on Coliru.
In the Coliru example - we need to indicate with a = delete
that the class does not have any other specialized definitions, nor a generalized definition.