【问题标题】:Call function with another function as argument within class使用另一个函数作为类中的参数调用函数
【发布时间】:2020-12-18 10:43:55
【问题描述】:

我试图构建代码来传递要在另一个函数中使用的函数。下面是我尝试过的代码,它在函数之外工作(所以如果我删除与类 Foo 相关的所有内容,它会工作),但我不知道如何让它在类本身内工作。如何在类中传递一个函数?我尝试了this->part1_math_classFoo::part1_math_classpart1_math_class,都没有成功。

// Example program
#include <iostream>
#include <string>
#include <stdint.h>
#include <functional>

int64_t loop(std::string partialMath, std::function<int64_t(std::string)> recurse)
{
    while (partialMath.find("(") != std::string::npos)
    {
        auto posClose = partialMath.find(")");
        auto posOpen = partialMath.rfind("(", posClose);

        std::string sub = partialMath.substr(posOpen + 1, posClose - posOpen - 1);

        int64_t resultInner = loop(sub, recurse);
        partialMath.replace(posOpen, sub.size() + 2, std::to_string(resultInner));
    }

    return recurse(partialMath);
}

int64_t part1_math(std::string math)
{
    return 0;
}

int64_t part2_math(std::string math)
{
    return 1;
}

class Foo {
public:
    Foo() { }
    
    int64_t loop_class(std::string partialMath, std::function<int64_t(std::string)> recurse)
    {
        while (partialMath.find("(") != std::string::npos)
        {
            auto posClose = partialMath.find(")");
            auto posOpen = partialMath.rfind("(", posClose);
    
            std::string sub = partialMath.substr(posOpen + 1, posClose - posOpen - 1);
    
            int64_t resultInner = loop_class(sub, recurse);
            partialMath.replace(posOpen, sub.size() + 2, std::to_string(resultInner));
        }
    
        return recurse(partialMath);
    }
    
    int64_t part1_math_class(std::string math)
    {
        return 2;
    }
    
    int64_t part2_math_class(std::string math)
    {
        return 3;
    }
    
    int64_t runLoop()
    {
        return loop_class("(1 + 2)", Foo::part1_math_class);
    }
};

int main()
{
  std::cout << loop("(1 + 2)", part1_math) << std::endl;  // this one works
  Foo bar;
  std::cout << bar.runLoop() << std::endl;  // this one does not even compile
  return 0;
}

【问题讨论】:

  • 是否要将成员函数作为参数发送给另一个函数?
  • 是的,但在同一个班级。我可以使用某种 if 逻辑来做同样的事情,但我只想传递函数(在同一个类中)来运行。
  • 为什么不使用 lambda? return loop_class("(1 + 2)", [this](std::string math) { return part1_math_class(math); }); std::function 可以与函数和仿函数一起使用。在这种情况下,lambda 提供匹配的函子(正确的签名)并绑定(捕获)this 作为成员。

标签: c++


【解决方案1】:

关于

    int64_t runLoop()
    {
        return loop_class("(1 + 2)", Foo::part1_math_class);
    }

Foo::part1_math_class() 在使用正确的对象调用时具有与std::function&lt;int64(std::string)&gt; 匹配的签名。 在这种情况下,this 将是指向预期对象的指针。因此,它必须以某种方式被“欺骗”到调用中。

一种简单的方法是使用functor 而不是函数。仿函数是一个重载operator() 的类,可以像函数一样工作,但具有额外的上下文。

std::function 参数可以接受函子和函数。

恕我直言,编写函子的最简单方法是lambda

    int64_t runLoop()
    {
        return loop_class("(1 + 2)",
          [this](std::string math) { return part1_math_class(math); });
    }

this 被捕获。签名匹配,因为参数是兼容的,并且返回类型是从 lambda 的主体中自动检测并匹配的。

Live Demo on coliru

【讨论】:

  • 很好,这完全符合我的要求。我真的需要正确阅读 lambdas 以了解如何使用它们。
【解决方案2】:

首先,要形成一个成员函数指针,你需要&amp;Foo::part1_math_class;注意&amp;

其次,当一个 n 元非静态成员函数指针存储在 std::function 中时,它充当 n+1 元函数,因为它需要一个隐式的第一个参数,该参数成为 this 指针。由于您的非静态成员函数是一元的,因此它适用于二进制 std::function。但是您的 std::function 也只是一元的。因此,不能使用非静态成员函数指针。

声明part1_math_class static 使其工作。

【讨论】:

  • 那我该如何使用 std::bind 呢?我也试过了,但也无法正常工作。
  • 那应该是另一个问题的话题。
  • std::function 的优点是它也可以与仿函数一起使用。因此,您可以通过函数成员提供额外的上下文。 this 可能是此上下文的一部分。因此,我不明白您为什么推荐 Declare part1_math_class static 使其工作。
  • 好吧,假设我在当前类的 part1_math 中使用成员变量,我不能使用静态函数(我的真实场景就是这种情况,我只是认为这个例子可以说明问题)跨度>
  • 那么你必须像@Sheff所说的那样:将你的非静态成员函数的第一个参数绑定到一个对象。在现代代码中,最简单的方法是使用 lambda 表达式。
【解决方案3】:

您可以参数化接受回调的函数,这样它们就不需要std::function,而是可以获取其他类型的可调用对象。

然后,有两个选择是将this 绑定到成员函数指针,或者使用 lambda:

// ...
template<typename F>
int64_t loop(std::string partialMath, F recurse)
{
    // ...
}

// ...

class Foo {
public:
    Foo() { }
    
    template<typename F>
    int64_t loop_class(std::string partialMath, F recurse)
    {
        // ...
    }
    
    // ...
    
    int64_t runLoop()
    {
        using namespace std::placeholders;
        return loop_class("(1 + 2)", std::bind(&Foo::part1_math_class, this, _1));
    }
    
    int64_t runLoopLambda() {
        return loop_class("(1 + 2)", [this](auto&& ...args) {
            return this->part1_math_class(std::forward<decltype(args)>(args)...);
        });
    }
};

// ...

【讨论】:

    【解决方案4】:

    您可以将loop_class 设为模板,然后使用简单的bind 或lambda。 (一般来说,lambda 应该比 bind 更受欢迎,但在这种情况下,我发现使用 bind 更简单)。例如:

    int64_t loop_class(std::string partialMath, std::function<int64_t(std::string)> recurse)
    {...}
    

    或模板

    template <typename F>
    int64_t loop_class(std::string partialMath, F recurse)
    {...}
    

    然后

    int64_t runLoop()
    {
        return loop_class("(1 + 2)",  bind(&Foo::part1_math_class, this, std::placeholders::_1));
    }
    

    See live

    【讨论】:

      猜你喜欢
      • 2018-11-13
      • 2016-09-23
      • 1970-01-01
      • 2021-05-02
      • 2015-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-09
      相关资源
      最近更新 更多