Идиоматическим способом было бы вывести тип вызываемого, как если бы это был какой-либо тип, и не заботиться о шаблонности вещи:
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);