#include <type_traits>
#include <utility>

    namespace util
    {
        template <typename T>
        struct always_void
        {
            typedef void type;
        };
    }

    namespace detail
    {
        template <typename T, typename Args, typename Enable = void>
        struct is_callable_impl
          : std::false_type
        {};

        template <typename T, typename... A>
        struct is_callable_impl<T, void(A...)
          , typename util::always_void<
                decltype(std::declval<T>()(std::declval<A>()...))
            >::type
        > : std::true_type
        {};

        template <typename T, typename C>
        struct is_callable_impl<T, void(C)
            , typename util::always_void<
                  decltype(
                      (std::declval<typename std::remove_pointer<C>::type>()
                          .*std::declval<T>()))
              >::type
        > : std::true_type
        {};

        template <typename T, typename C, typename... A>
        struct is_callable_impl<T, void(C, A...)
            , typename util::always_void<
                  decltype(
                      (std::declval<typename std::remove_pointer<C>::type>()
                          .*std::declval<T>())(std::declval<A>()...))
              >::type
        > : std::true_type
        {};
    }

    template <typename T, typename... A>
    struct is_callable
      : detail::is_callable_impl<T, void(A...)>
    {};

struct X {};

int main()
{
    static_assert(is_callable<void (X::*)(int) const, X&, int>::value, "increment::call");

    auto lambda = [](){};
    static_assert(is_callable<decltype(lambda)>::value, "");
    static_assert(is_callable<void()>::value, "");

    static_assert(is_callable<int (X::*)(double), X*, float>::value, "");
    static_assert(!is_callable<int (X::*)(double), X const*, float>::value, "");
    static_assert(is_callable<int (X::*)(double), X&, float>::value, "");
    static_assert(!is_callable<int (X::*)(double), X const&, float>::value, "");
    static_assert(is_callable<int (X::*)(double) const, X*, float>::value, "");
    static_assert(is_callable<int (X::*)(double) const, X const*, float>::value, "");
    static_assert(is_callable<int (X::*)(double) const, X&, float>::value, "");
    static_assert(is_callable<int (X::*)(double) const, X const&, float>::value, "");

    static_assert(is_callable<int (X::*), X*>::value, "");
    static_assert(is_callable<int (X::*), X const*>::value, "");
    static_assert(is_callable<int (X::*), X&>::value, "");
    static_assert(is_callable<int (X::*), X const&>::value, "");
    static_assert(is_callable<int (X::*), X&&>::value, "");
    static_assert(is_callable<int (X::*), X const&&>::value, "");

    static_assert(!is_callable<void(int&), int>::value, "");
    static_assert(is_callable<void(int&), int&>::value, "");
    static_assert(!is_callable<void(int&), int const&>::value, "");
    static_assert(!is_callable<void(int&), int&&>::value, "");
    static_assert(!is_callable<void(int&), int const&&>::value, "");

    static_assert(is_callable<void(int const&), int>::value, "");
    static_assert(is_callable<void(int const&), int&>::value, "");
    static_assert(is_callable<void(int const&), int const&>::value, "");
    static_assert(is_callable<void(int const&), int&&>::value, "");
    static_assert(is_callable<void(int const&), int const&&>::value, "");

    static_assert(is_callable<void(int&&), int>::value, "");
    static_assert(!is_callable<void(int&&), int&>::value, "");
    static_assert(!is_callable<void(int&&), int const&>::value, "");
    static_assert(is_callable<void(int&&), int&&>::value, "");
    static_assert(!is_callable<void(int&&), int const&&>::value, "");

    static_assert(is_callable<void(int const&&), int>::value, "");
    static_assert(!is_callable<void(int const&&), int&>::value, "");
    static_assert(!is_callable<void(int const&&), int const&>::value, "");
    static_assert(is_callable<void(int const&&), int&&>::value, "");
    static_assert(is_callable<void(int const&&), int const&&>::value, "");

    return 0;
}
