Оболочка для шаблонных функций - PullRequest
2 голосов
/ 23 мая 2019

Я пытаюсь выполнить следующее:

// or any templated function
template <typename... Args>
void function(Args... args) {}

// wrapper
void launch(???) { ??? }

int main()
{
    // first option
    launch(function, 1, 2, 3, 4);
    // second option
    launch<function>(1, 2, 3, 4);
}

Насколько я могу судить, первый вариант невозможен, так как мне пришлось бы пропустить специальную функцию шаблона (которую я пытаюсь выполнить).чтобы избежать).

Для второго варианта, я не знаю, возможно ли это, я придумал следующую неработающую реализацию:

template <template <typename...> class Function, typename... Args>
void launch(Args... args)
{
    Function<Args...>(args...);
}

, которая заканчиваетсяи дал мне:

main.cpp:18:5: error: no matching function for call to 'launch'
    launch<function>(1, 2, 3, 4);
    ^~~~~~~~~~~~~~~~
main.cpp:9:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Function'
void launch(Args... args)
     ^
1 error generated.

Так что-нибудь подобное возможно?

Ответы [ 3 ]

3 голосов
/ 23 мая 2019

Как насчет:

template <typename ... Args>
void launch(void(*func)(Args...), Args&&... args) {
    func(std::forward<Args>(args)...);
}

вызов launch:

launch(function, 1, 2, 3, 4);

Живой пример

3 голосов
/ 23 мая 2019

В принципе вы ничего не можете сделать с шаблонами функций, кроме , вызывайте их (и позволяйте выводить аргументы) или создайте их экземпляр (указав аргументы шаблона вручную).

Я полагаю, что существуют также нишевые ситуации, в которых аргументы шаблона могут быть выведены и конкретная реализация выбрана без фактического вызова, но здесь они не помогают Ответ AMA показывает, как это сделать!

Общие лямбды могут или не могут помочь вам решить вашу проблему, но вам нужна одна такая пересылка лямбда для каждого шаблона функции, который вы хотите сделать "сносным":

#include <functional>

// or any templated function
template <typename Arg1, typename Arg2>
void function(Arg1 arg1, Arg2 arg2) {}

int main()
{
    auto wrapper = [](auto arg1, auto arg2) {
        return function(arg1, arg2);
    };

    std::invoke(wrapper, 1, 2);
}

Демо

(Совершенная пересылка в функцию с переменным числом с лямбдой с переменным значением будет более сложной.)

Таким образом, вы могли бы также написать шаблоны функций в виде структур функторов или в форме лямбда-выражений, возвращаемых из не шаблонных функций.

1 голос
/ 23 мая 2019

Идиоматическим способом было бы вывести тип вызываемого, как если бы это был какой-либо тип, и не заботиться о шаблонности вещи:

template <typename F, typename ... Args>
auto launch(F f, Args&&... args) -> decltype(auto) {
    return f(std::forward<Args>(args)...);
}

Также будет пересылать возвращаемое значение функции.

Затем, чтобы отправить свою шаблонную функцию, вы должны поднять функцию в лямбду:

auto function_lift = [](auto&&... args) 
    noexcept(noexcept(function(std::forward<decltype(args)>(args)...)))
    -> decltype(function(std::forward<decltype(args)>(args)...))
{
    return function(std::forward<decltype(args)>(args)...);
};

// also works with defaulted parameters.
launch(function_lift, 1, 2, 3, 4); 

Создание этих поднятых функций очень многословно. Ответ на многословность в этом случае, конечно, макрос:

#define LIFT(lift_function) [](auto&&... args)                                \
    noexcept(noexcept(lift_function(std::forward<decltype(args)>(args)...)))  \
    -> decltype(lift_function(std::forward<decltype(args)>(args)...))         \
{                                                                             \
    return lift_function(std::forward<decltype(args)>(args)...);              \
}

Теперь вы можете позвонить вашей оболочке:

launch(LIFT(function), 5, 4, 3, 2);
...