【问题标题】:Why is std::bind not working without placeholders in this example (member function)?为什么 std::bind 在这个例子(成员函数)中没有占位符就不能工作?
【发布时间】:2014-04-20 18:09:18
【问题描述】:

例如,这是我的成员函数(do_it):

class oops
{
public:
    void do_it(GtkWidget *widget, GdkEvent *event, gpointer data)
    {
        g_print ("Hi there :)\n");
    }
};

...我使用std::bind 使它看起来像一个非成员函数:

oops o;
std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o);

但它不起作用,以下是编译器错误消息:

program.cc: In function ‘int main(int, char**)’:
program.cc:69:85: error: conversion from ‘std::_Bind_helper<false, void (oops::*)(_GtkWidget*, _GdkEvent*, void*), oops&>::type {aka std::_Bind<std::_Mem_fn<void (oops::*)(_GtkWidget*, _GdkEvent*, void*)>(oops)>}’ to non-scalar type ‘std::function<void(_GtkWidget*, _GdkEvent*, void*)>’ requested
   std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o);
                                                                                     ^

我必须使用std::placeholders修复它:

oops o;
std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

为什么不指定std::placeholders就不行?

【问题讨论】:

    标签: c++ function c++11 stdbind


    【解决方案1】:

    std::bind() 旨在创建一个可调用实体,代表对函数的(部分)调用。它将调用的一些参数绑定到生成的调用对象,并让其余参数在调用时指定:

    void f(int,int,int);
    
    int main()
    {
        std::function<void()> f_call = std::bind( f , 1 , 2 , 3);
    
        f_call(); //Equivalent to f(1,2,3)
    }
    

    std::bind()的第一个参数是要调用的函数,其余的是调用的参数。

    在本例中,调用对象是在指定所有三个参数的情况下生成的,因此调用点没有参数。现在考虑一个部分定义的调用:

    std::function<void(int,int,int)> f_call = std::bind( f );
    

    这不会编译,因为函数有三个参数,而你没有指定任何参数!这没有意义,对吧?如果你有一个带三个参数的函数,你应该将三个参数传递给调用对象。

    如果您需要指定必须在调用点指定某些参数,则必须使用占位符来表示该参数。例如:

    using namespace std::placeholders;
    
    std::function<void(int,int,int)> f_call = std::bind( f , _1 , _2 , _3 );
    
    f_call( 1 , 2 , 3 ); //Same as f(1,2,3)
    

    如您所见,我们使用占位符为函数调用指定三个“空格”,即在调用点指定的三个参数。
    请注意,占位符的编号指定调用点处的参数编号。调用点的第一个参数由_1标识,第二个由_2标识,以此类推。这可用于以不同方式指定参数,重新排序函数调用的参数等。例如:

    std::function<void(int,int)> f_call = std::bind( f , _1 , 2 , _2 );
    
    f_call( 1 , 3 ); //Equivalent to f( 1 , 2 , 3 );
    
    std::function<void(int,int,int)> reordered_call = std::bind( f , _3 , _2 , _1 );
    
    reordered_call( 3 , 2 , 1 ); //Same as f( 1 , 2 , 3 );
    

    最后,std::bind() 可用于将成员函数绑定到用于调用它的对象:

    struct foo
    {
        void f() const;
    };
    
    int main()
    {
        foo myfoo;
        std::function<void()> f = std::bind( &foo::f , std::cref( myfoo ) );
    
        f(); //Tah dah!
    }
    

    可以将成员函数视为具有一个隐藏参数的函数,该参数是完成调用的对象。这就是对象被绑定为第一个参数的原因。

    但是,就像上面的例子一样,如果你在绑定点只知道一定数量的参数,并且需要稍后在调用点指定其他参数,你应该使用占位符:: p>

    using namespace std::placeholders;
    
    oops o;
    
    std::function<GtkWidget*,GtkEvent*,gpointer> do_it = std::bind( &oops::do_it , std::ref( o ) , _1 , _2 , _3 );
    
    do_it( /* first param */ , /*second param */ , /* third param */ ); //Call
    

    一些细节

    调用对象的签名

    请注意,我们使用std::function 来存储调用对象。该函数的签名取决于生成的调用对象的类型,即取决于您在绑定点指定参数的方式

    调用对象只是另一个可调用实体,它充当对原始函数的调用。接下来是我们的f() 函数示例:

    std::function<void()> f_call = std:bind( f , 1 , 2 , 3 );
    

    这里调用对象的签名是void(),因为我们已经在绑定点指定了空洞组参数,在调用点没有留待指定(所以调用对象是没有参数的) .

    在部分调用的情况下:

    std::function<void(int,int,int)> f_call = std::bind( f, _1 , _2 , _3 );
    
    f_call( 1 , 2 , 3 );
    

    调用对象的签名是void(int,int,int),因为我们在调用点留下了三个要指定的参数(注意占位符)。通常调用对象具有与您在绑定点指定的占位符相同数量的参数。

    【讨论】:

    • 为什么std::bind 不做“显而易见的事情”,并要求可调用实体所需且未提供给std::bind 的任何参数在调用点作为尾随参数。 (顺便说一句,我希望这会比占位符的拼写错误更少。)
    • "因为我们在绑定点指定了hole参数集",有错别字,"hole"应该是"whole"。
    【解决方案2】:

    请注意,占位符的编号指定了调用点处的参数编号。调用点的第一个参数由_1标识,第二个由_2标识,以此类推。

    我想为 @Manu343726 添加一些示例。 如果您尝试将具有 3 个参数的函数转换为具有 2 个参数的函数。您可以在绑定时指定 1 个参数,并在调用时提供其余 2 个。

    typedef std::function<void(int, int)> add2;
    int add3(int x1, int x2, int x3) { return x1 + x2 + x3; }
    auto fn = std::bind(add3, 11, std::placeholders::_1, std::placeholders::_2);
    fn(22, 33)
    

    现在 11 是 add3 的 x1,_1 是 add3 的 x2,_2 是 add3 的 x3。 _2 表示fn 在调用时需要指定 2 个参数。 而且这种格式是错误的:

    auto fn = std::bind(add3, 11, std::placeholders::_2, std::placeholders::_3);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多