如何使用成员函数初始化`std::function`?

11

我正在学习std::function,以下是我的代码:

#include <iostream>
#include <functional>

struct Foo {
    void print_add(int i){ 
      std::cout << i << '\n'; 
    }
};

typedef std::function<void(int)> fp;

void test(fp my_func)
{
  my_func(5);
}

int main(){
    Foo foo;
    test(foo.print_add);
    return 0;
}

编译器错误:

 error: cannot convert 'Foo::print_add' from type 'void (Foo::)(int)' to type 'fp {aka std::function<void(int)>}'
     test(foo.print_add);

如何使这个功能起作用,即如何将成员函数作为参数传递?


可能是此问题的重复:在一个类中使用通用的std :: function对象调用成员函数 - sudo rm -rf slash
3个回答

16

print_addfoo 的非静态成员函数,这意味着它必须在 Foo 的实例上调用;因此它有一个隐式的第一个参数 this 指针。

使用一个 lambda 表达式,捕获 foo 实例并在其上调用 print_add

Foo foo;
test([&foo](int i){ foo.print_add(i); });

另一种选择是使用 std::bind 绑定 foo 实例:

test(std::bind(&Foo::print_add, &foo, std::placeholders::_1));

现场演示


你能解释一下这是如何工作的吗?比如说,如果这是第一个参数,那么为什么在bind(...)中它是第二个参数呢? - Kam
1
@Kam bind 的第一个参数是指向成员函数的指针。第二个参数及以后的参数是在调用 std::function 时传递给该成员函数的参数。因此,在 test 中调用可调用对象时,指向 foo 的指针将作为第一个参数(即 this 指针)传递给 print_addplaceholders::_1 表示您稍后会提供该参数,并且您提供给可调用对象的第一个参数(即 5)将作为第二个参数传递给 print_add(因为占位符出现的位置是第二个参数的位置)。 - Praetorian
@Kam 这个页面提供了一个很好的使用bind的例子,并展示了如何使用移动占位符来实现不同的结果。 - Praetorian
最后一个问题 :) 在这里哪个更好?lambda还是bind?就性能而言? - Kam
@Kam,你将两者都放入了一个std::function中,使用它会有一些性能开销,这个开销应该与使用虚函数调用相当。bind本身不应该增加任何开销,但说实话,我不确定。在这种情况下,我个人会使用lambda表达式。 - Praetorian

9

你需要一个对象来调用非静态成员函数。因此,当你想要获取一个std::function时,有几个选择:

  • 将方法设置为静态
  • 通过lambda将对象绑定到std::function
  • 通过自定义函数对象将对象绑定到std::function
  • 在调用std::function时传递对象

#include <iostream>
#include <functional>

struct A {
    int i = 42;
    int get() const { return i; }    
    
    static int get_static() { return 0; }
};

struct A_functor {
    A a;    
    int operator()() const { return a.get(); }
};

int main() {    

    // static method
    std::function<int()> f1 = &A::get_static;
    std::cout << f1() << "\n";

    // bind an object to the function via lambda
    A a;
    std::function<int()> f2 = [&a](){ return a.get(); };
    std::cout << f2() << "\n";

    // store the object in the std::function
    std::function<int()> f3 = A_functor{};
    std::cout << f3() << "\n";
    // or 
    std::function<int()> f4 = [a = A()](){ return a.get(); };
    std::cout << f4() << "\n";

    // pass the object when the std::function is called
    std::function<int(A&)> f5 = &A::get;
    std::cout << f5(a) << "\n";
    // or 
    std::function<int(A*)> f6 = &A::get;
    std::cout << f6(&a) << "\n";
    
}

4
问题

你不能直接将属于类型 Foo 成员函数指针绑定到 std :: function< void(int)>,具体原因是调用非静态的成员函数需要一个 Foo 类型的实例。

Foo obj; obj.member_function (); // can't call `member_function` without `obj`

注意:您可以将 `&Foo::print_add` 绑定到 `std::function x;`,并调用 `x(instance_of_Foo, arg);`。
注意:也可以将其绑定到 `std::function`,这需要一个类型为 `Foo` 的左值的 `Foo*`。
解决方法是使用 `std::bind` 将 `Foo` 类的实例绑定到相关成员函数,例如以下示例:
int main(){
    Foo foo;
    test (std::bind (&Foo::print_add, foo, std::placeholders::_1));
    return 0;
}

以上,我们将一个名为fooFoo实例绑定到成员函数指针&Foo::print_add上。
使用std::placeholders::_1告诉std::bind生成一个可接受一个(1)参数的函数对象。
使用以上代码片段,您所期望的行为是被实现的;my_func(5)将等同于调用foo.print_add (5)
文档:

2
第一句话不太准确,您不能将Foo::print_add绑定到std::function<void(int)>,但可以将其绑定到std::function<void(Foo&, int)>std::function<void(Foo*, int)> - Jonathan Wakely

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接