【问题标题】:How to initialize `std::function` with a member-function?如何使用成员函数初始化`std::function`?
【发布时间】:2014-05-30 19:22:06
【问题描述】:

我正在努力学习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);

我怎样才能做到这一点,即如何将成员函数作为参数传递?

【问题讨论】:

标签: c++ c++11


【解决方案1】:

您需要一个对象来调用非静态成员函数。因此,当您想获得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
    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";
    
}

【讨论】:

    【解决方案2】:

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

    使用捕获 foo 实例并在其上调用 print_add 的 lambda。

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

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

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

    Live demo

    【讨论】:

    • 你能解释一下这是如何工作的吗?即如果这是第一个参数,那么它为什么是 bind(...) 中的第二个参数?
    • @Kam bind 的第一个参数是指向成员函数的指针。后面的第二个参数是当您调用std::function 时传递给该成员函数的参数。因此,当您在 test 中调用 callable 时,指向 foo 的指针作为第一个参数传递给 print_add(this 指针)。 placeholders::_1 意味着您稍后将提供该参数,并且您提供给可调用对象 (5) 的第一个参数将作为第二个参数传递给 print_add(因为这是占位符出现的位置) .
    • @Kam This page 提供了一个很好的使用 bind 的示例,并展示了如何使用移动占位符来实现不同的结果。
    • 最后一个问题 :) 这里首选哪一个? lambda 或绑定?性能明智?
    • @Kam 您将两者都放入std::function,并且有一个与使用其中之一相关的performance overhead,这应该与使用虚函数调用大致相同。 bind 本身不应该增加任何开销,但老实说,我不知道。在这种情况下,我个人会使用 lambda。
    【解决方案3】:

    问题

    您不能直接将属于Foo 类型的成员函数指针 绑定到std::function&lt;void(int)&gt;,特别是因为调用非静态成员函数 需要一个实例类型为Foo。

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

    注意:但是,您可以将&amp;Foo::print_add 绑定到std::function&lt;void(Foo&amp;, int)&gt; x;,并将其称为x(instance_of_Foo, arg);。

    注意:也可以将其绑定到std::function&lt;void(Foo*, int&gt;,这需要Foo* 而不是Foo 类型的左值。



    解决方案

    相反,您可以使用std::bind 将Foo 的实例绑定到有问题的成员函数,如下例所示:

    int main(){
        Foo foo;
        test (std::bind (&Foo::print_add, foo, std::placeholders::_1));
        return 0;
    }
    

    上面我们将一个名为foo 的Foo 实例绑定到成员函数指针 &amp;Foo::print_add。


    std::placeholders::_1 的用法告诉std::bind,我们希望它生成一个可使用一 (1) 个参数调用的函数对象。

    使用上面的 sn-p 你将得到你当前要求的行为; my_func(5) 相当于调用 foo.print_add (5)。


    文档

    【讨论】:

    • 第一句不太准确,不能将Foo::print_add绑定到std::function&lt;void(int)&gt;,但可以绑定到std::function&lt;void(Foo&amp;, int)&gt;或std::function&lt;void(Foo*, int)&gt;
    猜你喜欢
    • 2021-06-13
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    • 2015-11-24
    • 1970-01-01
    相关资源
    最近更新 更多