【问题标题】:Passing a math function as method's input parameter C++将数学函数作为方法的输入参数传递 C++
【发布时间】:2017-09-09 18:24:57
【问题描述】:

我正在尝试编写一个通用积分函数,并且我想以这样一种方式实现它,以便它可以接受任何数学函数。也就是说,我想将数学函数作为输入参数传递。在伪代码中:simpson_int(x*x)。我听说过<functional> 中的function 模板,但我对C++ 中的模板并没有真正的经验。

【问题讨论】:

  • 这是在 C 中,但我想它也适用(在 C++ 中你有更多的可能性):stackoverflow.com/questions/9410/…
  • 首先使用一个硬编码的函数来编写它。一旦你完成了这个工作,考虑如何让它接受一个函数作为参数。
  • 我很确定你可以使用simpson_int([](double x) { return x * x; }),它会像函数指针一样工作。
  • @PeteBecker: 我可以做类似simpson_int( double (*f) (double,double) 然后说simpson_int(powx,2)

标签: c++ function templates lambda


【解决方案1】:

我想到了一些解决方案(这是我解决问题的方法,当然还有更多解决方案,也许我指出的不是最好的),考虑到您需要的事实在 Simpson 实现中多次调用参数函数(因此您需要一个“可调用”参数):

函数指针

函数指针(C 多于 C++),在其中使用两个参数声明:第一个是指向具有指定类型的函数的指针,而第二个是函数的参数。让我们 举个例子:

#include <iostream>

double power2(double x) {
  return x * x;
}

double simspon(double (*f)(double), double x) {
  return f(x);
}

int main() {
  std::cout << simspon(power2, 2);
  return 0;
}

在这种情况下,我没有使用任何模板来获得结果。但这将不会将任何函数作为第一个参数,而只会将一个以 double 作为参数并返回 double 的函数。

我认为大多数 c++ 程序员会建议你避免使用这种方法。

函数指针和模板

因此,您可能希望使用模板扩展前面的示例并使其更通用。重新定义函数非常简单 接受仅在代码中使用时才实际指定的模板(抽象类型):

#include <iostream>

double power2(double x) {
  return x * x;
}

int power2int(int x) {
  return x * x;
}

template <class T, class P>
P simspon(T (*f)(P), P x) {
  return f(x);
}

int main() {
  std::cout << simspon<double, double>(power2, 2.0);
  std::cout << simspon<int, int>(power2int, 2);
  return 0;
}

TP是两个模板:第一个用于描述函数指针的返回值,第二个指定函数指针的参数,simpson的返回值。所以当您编写template &lt;class T, classP&gt; 时,您实际上是在通知编译器您正在使用 T 和 P 作为不同类型的占位符。稍后,当您将调用main 中的函数时,您实际上将声明您想要的类型。 这不是好的代码,但我正在构建理解模板的路径。此外,您在实际调用 simpson 时指定参数函数的类型,使用 &lt; &gt;

(免责声明:你应该考虑使用template &lt;typename T ...&gt;而不是class。但是我用的是旧的class,有些情况typename不能用,有有很多关于 SO 的问题可以深入探讨。)

使用std::function

您可能希望创建一个变量来存储要作为simpson 的参数传递的函数,而不是使用函数指针作为参数。这带来了几个优点,因为它们实际上是代码中的一个对象,在某些不需要的情况下具有一些可预测的行为(例如,在空函数指针的情况下,您必须检查指针本身并处理它,以防@987654336 @如果没有可调用的指针,它会抛出std::bad_function_call错误)

这里是一个例子,它再次使用模板,和以前一样:

#include <iostream>
#include <functional>

double power2(double x) {
  return x * x;
}

int power2int(int x) {
  return x * x;
}

template <class T, class P>
P simspon(std::function<T(P)> f, P x) {
  return f(x);
}

int main() {
  std::function<double(double)> p_power2 = power2; 
  std::cout << simspon<double, double>(p_power2, 2.0);

  std::function<double(double)> p_power2int = power2int; 
  std::cout << simspon<int, int>(power2int, 2);
  return 0;
}

使用 lambdas

lambdas 是闭包,在您的情况下(如果您可以使用标准 C++14)可以与 auto 关键字一起使用以实现相当普遍的行为,而无需显式使用模板。闭包还能够捕获部分/整个上下文,check the reference 为此。

让我们看一个示例,其中我创建了两个接收不同参数的 lambdas 和一个非常通用的 simpson 函数(实际上不是,编译器是否针对您所做的调用定义了不同的函数) .

#include <iostream>

auto lambda = [](auto x) { return x * x ; };
auto lambda_2 = [] (int x) { return x + 10; };

auto simpson(auto f, auto x) {
  return f(x);
}

int main() {
  std::cout << simpson(lambda, 2.0);
  std::cout << simpson(lambda_2, 1);
  return 0;
}

您需要使用-std=c++14 标志编译它。我有很多建议建议您避免以这种方式实现代码,请记住它仅具有一些说明性目的(我用 auto 关键字夸大了)。

函数对象(问题类)

也许对您的情况的改进是为数学函数编写一个通用类,以集成并将对象传递给您的函数。这带来了几个优点:您可能希望将一些综合结果保存在您的函数中,或者甚至编写流运算符来漂亮地打印您的问题。这是数学库通常采用的解决方案。

在这个极其简单的例子中,我们有一个有问题的类。当您为此类创建新实例时,std::function 将传递给构造函数并存储在类中。该类的实例是您的simpson 的参数:

#include <iostream>
#include <functional>

template <class T, class P>
class Problem {
public:
  // Attributes
  std::function<T(P)> _f;

  // Constructor
  Problem(std::function<T(P)> f) : _f(f) {};

  // Making the object callable
  P operator()(P x) { return _f(x); }
};

template <class T, class P>
P simspon(Problem<T, P> p, P x) {
  return p(x);
}

int main() {
  Problem<double, double> prb([](double x) { return x * x; });
  std::cout << simspon<double, double>(prb, 2);
  return 0;
}

【讨论】:

    【解决方案2】:

    使用std::function,例如:

    #include <iostream>     // std::cout
    #include <functional>   // std::function
    
    int main()
    {
        std::function<double(double)> simpson_int =([](double x) { return x * x; };
        std::cout << "simpson_int(4): " << simpson_int(4) << '\n';
        return 0;
    }
    

    哪个输出:

    simpson_int(4): 16

    【讨论】:

    • 对不起,但我认为 OP 想要构建一个 Simpson 集成方案,因此他希望 simpson_int 接受一个闭包作为参数,而不是“作为一个闭包”。
    • 嗯 @MatteoRagni 我不确定我是否理解,你能帮助改进答案吗?
    猜你喜欢
    • 1970-01-01
    • 2011-09-13
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    • 2018-09-09
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多