c++ 为什么std :: bind在这个例子中没有占位符(成员函数)不起作用?

例如,这是我的成员函数(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 :: placeholder来修复它:

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 ::占位符的情况下不起作用?

最佳答案
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!
}

成员函数可以被看作具有一个隐藏参数的函数,它是调用完成的对象.这就是为什么将对象绑定为第一个参数.

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

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 ::函数存储调用对象.该函数的签名取决于生成的调用对象的类型,也就是说,取决于在绑定点指定参数的方式.

调用对象只是作为对原始函数的调用的另一个可调用实体.遵循我们的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),因为我们已经在调用点留下了三个要指定的参数(注意占位符).通常,调用对象具有与在绑定点指定的占位符相同数量的参数.

转载注明原文:c++ 为什么std :: bind在这个例子中没有占位符(成员函数)不起作用? - 代码日志