Переслать шаблон авто - PullRequest
       39

Переслать шаблон авто

0 голосов
/ 22 февраля 2019

Контекст

(1) Можно извлечь тип возвращаемого значения и типы аргументов вызываемого объекта со следующей чертой:

#include <tuple>

template<class T>
struct callable_trait
{};

template<class R, class... Args>
struct callable_trait<R(Args...)>
{
    using return_type    = R;
    using argument_types = std::tuple<Args...>;
};

(2) Начиная с C ++ 17, одинможно определить шаблон с помощью template<auto>:

Если параметр шаблона объявлен auto, его тип выводится из соответствующего аргумента.

Вопрос

(3) Затем я смогу предоставить немного синтаксического сахара:

template<auto callable>
using return_type = typename callable_trait<decltype(callable)>::return_type;

... но он не работает слишком хорошо ...

void f();
void g(return_type<f>);
error: no type named 'return_type' in 'callable_trait<void (*)()>'
using return_type = typename callable_trait<decltype(callable)>::return_type;
^~~~~

Лямбда не помогает ...

auto lambda= [](){};
void h(return_type<lambda>);
error: a non-type template parameter cannot have type '(lambda at <source>:19:14)'
void h(return_type<lambda>);
                   ^

Живая демоверсия

Как мне это обойти?

1 Ответ

0 голосов
/ 22 февраля 2019

В случае функции проблема заключается в том, что 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");
...