包装模板功能

时间:2019-05-23 10:32:30

标签: c++ templates metaprogramming

我要完成的工作如下:

// 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 个答案:

答案 0 :(得分:3)

除了调用它们(并推导出参数)或实例化它们(通过手动指定模板参数)之外,您基本上不能对函数模板做任何事情。

我相信在某些特殊情况下,无需实际调用就可以推导出模板参数并选择特定的实例化,但是在这里无济于事 AMA的答案显示了如何做到这一点!

通用lambda可能会或可能无法帮助您解决问题,但您需要为每个要使其“通过”的功能模板提供一个这样的转发lambda:

#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);
}

Demo

(使用可变参数lambda完美地转发到可变函数)。

因此,您最好首先以函子结构的形式或从非模板函数返回的lambda形式编写函数模板。

答案 1 :(得分:3)

怎么样:

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

呼叫launch

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

Live example

答案 2 :(得分:1)

惯用的方法是推断可调用对象的类型,就像它是任何类型一样,而不在乎事物的模板性:

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

它还将转发函数的返回值。

然后,要发送模板化函数,必须将函数提升为lambda:

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);