В случае функции проблема заключается в том, что decltype(callable)
для функции возвращает указатель на функцию, которая не соответствует вашей специализации.С лямбдой вы получаете тип лямбды, а не operator()
.У вас будет такая же проблема, если вы будете использовать функцию-член, так как ваша специализация не соответствует указателю на функцию-член.
Вам нужно что-то, что может принять все эти типы и дать вам R(Args...)
взамен.К счастью, у нас есть std::function
, и он создан именно для этого.Он имеет направляющие вычеты, которые позволят ему взять любой тип функции и сделать std::function<R(Args...)>
в соответствии с его сигнатурой.Используя std::function
, ваш код может стать
template<class T>
struct callable_trait
{};
template<class R, class... Args>
struct callable_trait<std::function<R(Args...)>>
{
using return_type = R;
using argument_types = std::tuple<Args...>;
static constexpr size_t argument_count = sizeof...(Args);
};
template<auto callable>
using return_type = typename callable_trait<decltype(std::function{callable})>::return_type;
template<auto callable>
static constexpr size_t argument_count = callable_trait<decltype(std::function{callable})>::argument_count;
void f();
void g(return_type<f>);
auto lambda = [](){};
void h(return_type<lambda>);
void e(int, int, int);
static_assert(argument_count<e> == 3, "oh no");
, но это работает только на gcc head .Clang не может вывести std::function
и более ранние версии gcc и MSVS терпят неудачу по причине, подробно описанной здесь: Почему gcc завершается ошибкой при использовании лямбды для нетипичного параметра шаблона?
Есливы переключаетесь на принятие параметра типа и используете decltype
, это работает как на gcc, так и на MSVS, но clang все еще имеет проблемы с руководством по выводам
template<class T>
struct callable_trait
{};
template<class R, class... Args>
struct callable_trait<std::function<R(Args...)>>
{
using return_type = R;
using argument_types = std::tuple<Args...>;
static constexpr size_t argument_count = sizeof...(Args);
};
template<typename callable>
using return_type = typename callable_trait<decltype(std::function{std::declval<callable>()})>::return_type;
template<typename callable>
static constexpr size_t argument_count = callable_trait<decltype(std::function{std::declval<callable>()})>::argument_count;
void f();
void g(return_type<decltype(f)>);
auto lambda = [](){};
void h(return_type<decltype(lambda)>);
void e(int, int, int);
static_assert(argument_count<decltype(e)> == 3, "oh no");