允许可变参数计数的函数作为模板函数参数

Allow function of variadic argument count as template function argument

在 C++ 中,我们可以很容易地定义模板函数参数,如 this 问题中所示。但是,这只允许具有已定义参数列表和 return 值的函数。

我们可以像这样制作这些类型的模板参数:

template<typename R, typename P, R(*Func)(P)>
R proxy_func(P arg)
{
    // do some stuff...
    R&& res{ Func(std::forward(arg)) };
    // do some more stuff...
    return res;
}

但是,这里我们仍然限制在固定数量的参数。

像下面这样的尝试不会编译(至少在 MSVC 19.24.28315 上),因为 " 模板参数 'Func' 不能使用,因为它遵循模板参数包并且不能从'proxy_func'".

的函数参数推导出来
template<typename R, typename... P, R(*Func)(P...)>
R proxy_func(P... args)
{
    // do some stuff...
    R&& res{ Func(std::forward(args)...) };
    // do some more stuff...
    return res;
}

那么允许任意函数作为模板参数的选项是什么 - 同样重要的是 - 允许其 参数 用作函数模板的参数它用于(如上面的 proxy_func)?理想情况下,我将能够使用任何函数调用 proxy_func 作为(理想情况下 单个 )模板参数 Func 并能够应用其(proxy_func's) Func.

的参数

编辑: 我特别感兴趣的是有一个函数-实例作为模板参数(例如允许模板实例化proxy_func 以根据个人 Funcs).

应用编译时优化

从某种意义上说,您link的问答是复杂的方式。简单的方法是:

template <typename F>
void foo(F f) {
    f();    // call the function
}

在您的情况下,可以推导出 return 值,并且可以从 foo 的参数推导出参数类型。请注意,它们不必完全匹配 f 的参数类型,只要可以用它们调用 f 即可:

template <typename F,typename ... Args>
auto foo( F f, Args ... args) {
    return f(args...);    // call the function
}

为了简洁起见,我暂时省略了转发和存储调用结果,但答案仍然适用。

如果你想要函数本身,而不是它的类型作为模板参数(抱歉我先错过了那部分)这仍然有效:

template <auto f,typename ... Args>
auto foo(Args ... args) {
    return f(args...);    // call the function
}

但是,如果 f 是 lambda,这将不起作用。详情请看这里:https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter

感谢 C++17,auto 可以用作 non-type 模板参数的占位符,然后我们可以在没有类型声明的情况下传递函数指针:

template<auto func, typename... Args>
auto proxy_func(Args&&... args){
    // do something...
    auto result = func(std::forward<Args>(args)...);
    // do something...
    return result;
}

而且在 C++17 之前非常累人。如果你真的想要,你必须使用包装器:

int foo(){ return 0; };

template<typename Ret, typename... Args>
struct func_wrapper{
    template<Ret(*func)(Args...)>
    struct helper{
        typedef Ret return_type;
        typedef std::tuple<Args...> parameter_types;
        static constexpr Ret (*value)(Args...) = func;
    };
};
template<typename Ret, typename... Args>
auto helper(Ret(*)(Args...))->func_wrapper<Ret, Args...>;

template<typename Wrapper, typename... Args>
auto proxy_func(Args&&... args)
->decltype(Wrapper::value(std::forward<Args>(args)...)){
    // do something...
    auto result = Wrapper::value(std::forward<Args>(args)...);
    // do something...
    return result;
}

int main(){
    typedef decltype(helper(&foo)) wrapper_klass;
    typedef wrapper_klass::helper<&foo> wrapper_type;
    proxy_func<wrapper_type>();
}

然后,如果你想通过 func 决定 Args,你必须使用一些棘手的方法来解压参数包:

template<auto>
struct proxy_func_klass;
template<typename Ret, typename... Args, Ret(*func)(Args...)>
struct proxy_func_klass<func>{
    static Ret call(Args... args){
        return func(args...);
    }
};

和 C++11 版本:

int foo(){ return 0; };

template<typename Ret, typename... Args>
struct func_wrapper{
    template<Ret(*func)(Args...)>
    struct helper{
        typedef Ret return_type;
        typedef std::tuple<Args...> parameter_types;
        static constexpr Ret (*value)(Args...) = func;
    };
};
template<typename Ret, typename... Args>
auto helper(Ret(*)(Args...))->func_wrapper<Ret, Args...>;

template<typename, typename, typename>
struct helper_klass;
template<typename Self, typename Ret, typename... Args>
struct helper_klass<Self, Ret, std::tuple<Args...>>{
    static Ret call(Args... args){
        return Self::value(args...);
    }
};
template<typename Wrapper>
struct proxy_func_klass : public helper_klass<proxy_func_klass<Wrapper>, typename Wrapper::return_type, typename Wrapper::parameter_types>{
    static constexpr auto value = Wrapper::value;
};


int main(){
    typedef decltype(helper(&foo)) wrapper_klass;
    typedef wrapper_klass::helper<&foo> wrapper_type;
    proxy_func_klass<wrapper_type>::call();
}