将 n 个参数的函数作为 n-x 个参数的函数传递

Pass a function of n arguments as function of n-x arguments

这是一个非常基本的问题,我确定之前有人回答过这个问题,但我不知道要搜索什么。

声明我有一个集成了数学函数的函数:

double integrator(double (*func_to_integrate)(double,double));

但是我的积分函数属于允许我操作两个以上参数的类型,例如:

double func_to_integrate(double mu, double w0, double x, double y);

这样我就可以遍历 mu 和 w0 的不同值并比较积分结果。 如何将 func_to_integrate 之类的函数传递给集成商?

问候

编辑:正如 alain 在评论中指出的,这部分重复了:How can currying be done in C++?

有没有一个对函数指针进行柯里化操作的优雅解决方案?

How can I pass a function like func_to_integrate to integrator?

看起来很容易修复。只需在指针函数签名中再添加两个参数即可。

double integrator(double (*func_to_integrate)(double,double,double,double));

正如之前的评论所指出的,最优雅的解决方案是使用 bind 和/或 lambda。一个不错的解决方案是适配器设计模式 class 包装器,其中 mu 和 w0 成为 class 成员。

class IntegratorAdaptor {
  private:
    double _mu, double _w0;

  public:
    IntegratorAdapter(double arg_mu, double arg_w0)
    : _mu(arg_mu), _w0(arg_w0) { }

    double twoArgIntegrator( double x, double y )
      { return func_to_intergrate( _mu, _w0, x, y ); }
};

此 class 的构造开销非常低,因此我将成员设为不可变。我没有为 class 和函数想出很好的名字,你应该比我想出更多的名字。

鉴于您能够更改 integrator 函数的签名,有多种解决方案。基本的两个方向是

  1. 使用通用模板参数而不是函数指针(--调用者必须知道要传递的正确签名),或者
  2. 使用 std::function<double(double, double)> 作为函数参数。

这两种选择都允许您传递通用函数对象(函子、lambda、std::bind-对象等)。我会选择备选方案 1。因为它通常会提供更好的性能。

然后你可以很容易地设置一个lambda:

double mu = 1.0;
double w0 = 1.0;
auto f = [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };

并将 f 传递给您的(调整后的)integrator 例程。


如果您不能更改函数签名,这里还有一个替代方案——因为第三方库通常是这种情况。

我最初认为在这种情况下没有解决方案,因为您不能将通用仿函数绑定到函数指针。但后来我在 this answer 中遇到了一个好主意(我稍微调整了一下):根据静态 std::function 变量对所有内容进行编码,然后使用静态函数调用此 std::function 对象。由于静态函数只是全局函数的语法糖,所以可以设置一个指向它的函数指针:

template <typename Res, typename... Args>
struct function_ptr_helper
{
public:
    template<typename function_type>
    static auto bind(function_type&& f) { func = std::forward<function_type>(f); }

    static auto invoke(Args... args) { return func(args...); }
    static auto* ptr() { return &invoke; }

private:
    static std::function<Res(Args ...)> func;
};

template <typename Res, typename... Args>
std::function<Res(Args ...)> function_ptr_helper<Res, Args...>::func;

template <typename Res, typename ... Args>
auto* get_function_ptr(std::function<Res(Args...)> f)
{
    using type = function_ptr_helper<Res, Args...>;

    type::bind(std::move(f));
    return type::ptr();
}

DEMO

您可以将其用作

double mu = 1.0;
double w0 = 1.0;
std::function<double(double, double)> f
    = [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };

integrator(get_function_ptr(f));

但是请注意,您在这里处理的是全局变量。这通常有效,但有时可能会导致细微的错误(例如,当您在单个表达式中多次调用 get_function_ptr 时)。

我看到的大多数此类问题的答案都依赖于 std::function and/or C++ 模板。我想分享一个可能不太通用的替代解决方案,但对我来说更简单。 它不使用 std::function 或模板——事实上,它根本不使用任何库。

这个想法是传递一个实现特定 'interface' 的对象,而不是传递一个函数指针。在这个例子中,

double integrator(double (*func_to_integrate)(double,double))

变成

double integrator(Integratable func_to_integrate)

其中 Integratable 是一个 'interface'(抽象基础 class)定义为

class Integratable {
public:    
    virtual double compute(double x, double y) = 0;  // pure virtual function
}

然后我们可以将 func_to_integrate 变成这个 class 的一个实例,为额外的参数添加额外的成员:

class SomeClassName : public Integratable {
public:
    double compute(double x, double y);
    double mu;
    double w0;
}

SomeClassName func_to_integrate;

要在循环中测试 muw0 的多个值:

for(double mu : mus) {
    for(double w0 : w0s) {
        func_to_integrate.mu = mu;
        func_to_integrate.w0 = w0;
        integrator(func_to_integrate);
    }
}

当然,我们必须修改 integrator 以便它调用传递给它的对象上的 compute() 方法而不是调用函数指针,但这是微不足道的(假设你可以更改 integrator 的签名,这可能是解决此问题的任何可能方法所必需的)。

我喜欢这个解决方案,因为它避免了一些 C++ 更重量级的特性和库。但是,与通常建议在 C++ 中部分应用的许多其他解决方案相比,它肯定不那么通用。对于 OP,我相信这个解决方案非常适合给定的用例。