First of all you need to declare A as a full type (A<int>
for example).
It's not relyable to check if an object is invokeable or not just through unwrapping and comparing it's operator()
because it will fail on templated or overloaded functors.
Im using the following piece of code
to detect if an object is callable with a given function signature (ReturnType(Arguments...)
)
This works also for overloaded or templated operator()
's since the code tries to invoke the object with the given parameters
instead of comparing the signature of it's unwrapped operator()
.
As small addition the object can also be checked if it is invokeable from a const, volatile or rvalue context.
#include <type_traits>
template<typename Fn>
struct impl_is_callable_with_qualifiers;
template<typename ReturnType, typename... Args>
struct impl_is_callable_with_qualifiers<ReturnType(Args...)>
{
template<typename T>
static auto test(int)
-> typename std::is_convertible<
decltype(std::declval<T>()(std::declval<Args>()...)),
ReturnType
>;
template<typename T>
static auto test(...)
-> std::false_type;
};
template<bool Condition, typename T>
using add_const_if_t = typename std::conditional<
Condition,
typename std::add_const<T>::type,
T
>::type;
template<bool Condition, typename T>
using add_volatile_if_t = typename std::conditional<
Condition,
typename std::add_volatile<T>::type,
T
>::type;
template<bool Condition, typename T>
using add_lvalue_if_t = typename std::conditional<
Condition,
typename std::add_lvalue_reference<T>::type,
T
>::type;
template<typename T, typename Fn, bool Const, bool Volatile, bool RValue>
using is_callable_with_qualifiers = decltype(impl_is_callable_with_qualifiers<Fn>::template test<
add_lvalue_if_t<!RValue,
add_volatile_if_t<Volatile,
add_const_if_t<Const,
typename std::decay<T>::type>>>
>(0));
It is not possible to determine whether a type has any variant of overloaded operator()
,
however you can test for a specific type or perform tests for multiple types to detect "possible" templates.
An example which covers the code from your question would be:
#include <iostream>
template<typename T>
class A
{
public:
A() = default;
T operator()() { return T(); }
T operator()(T a) { return T(); }
T operator()(T a, T b) { return T(); }
};
template <typename TheClass, typename T>
using is_callable = is_callable_with_qualifiers<TheClass, T(T), false, false, false>;
int main()
{
std::cout << is_callable<A<int>, int>::value; // true
}
Demo