【问题标题】:Pass a function of n arguments as function of n-x arguments将 n 个参数的函数作为 n-x 个参数的函数传递
【发布时间】:2016-03-06 20:17:02
【问题描述】:

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

表示我有一个集成数学函数的函数:

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 在 cmets 中指出的那样,这部分是重复的:How can currying be done in C++?

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

【问题讨论】:

  • integrator调用func_to_integrate时,muw0的值应该是多少?您可能想查看lambda functionsstd::bind
  • 它们应该是一些我可以在循环中操作的双打。我尝试了 lambda 函数并绑定。对于这两种情况,我都无法创建集成商可以接受的对象。
  • 为什么你的函数和积分器不返回double
  • 你是对的,对不起。
  • 您必须更改integrator 的签名。使用它当前的签名,你唯一能做的就是使 muw0 成为全局变量,而不是额外的参数。

标签: c++ function c++11 currying reduction


【解决方案1】:

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

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

这两种选择都允许您传递通用函数对象(函子、lambda、std::bind-object 等)。我会选择替代 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 时)。

【讨论】:

    【解决方案2】:

    如何将 func_to_integrate 之类的函数传递给积分器?

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

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

    【讨论】:

      【解决方案3】:

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

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

      这个类的构造开销非常低,所以我让成员不可变。我没有为类和函数想出很好的名字,你应该比我多考虑这些名字。

      【讨论】:

        【解决方案4】:

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

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

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

        变成

        double integrator(Integratable func_to_integrate)
        

        其中Integratable 是一个“接口”(抽象基类),定义为

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

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

        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()方法,但这很简单(假设你可以更改@987654334的签名@,这可能是任何可能解决此问题的方法所必需的)。

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

        【讨论】:

          猜你喜欢
          • 2012-09-22
          • 1970-01-01
          • 2017-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-10
          • 2017-08-07
          • 1970-01-01
          相关资源
          最近更新 更多