Question

For example, this is my member function (do_it):

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

... and i use std::bind to make it look like a non-member function:

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

but it doesn't work, the following is the compiler errors message:

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);
                                                                                     ^

I have to fix it by using 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);

Why it doesn't work without specifying std::placeholders?

Était-ce utile?

La solution

std::bind() is designed to make a callable entity which represent a (partial) call to a function. It works binding some parameters of the call to the call object generated, and letting the rest of the parameters be speficied at the point of the call:

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)
}

The first parameter of std::bind() is the function to be called, and the rest are the arguments of the call.

In this example, the call object is generated with all the three parameters specified, so the call point has no parameters. Now consider a partial defined call:

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

This doesn't compile, because the function has three parameters and you have specified no one! That doesn't make sense, right? If you have a function with three parameters, you should pass three parameters to the call object.

If you need to specify that some parameters have to be specified at the call point, you have to use placeholders to represent that parameters. For example:

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)

As you can see, we used placeholders to specify three "spaces" for the function call, i.e. three parameters that would be specified at the call point.
Note that the numbers of the placeholders specify the number of the parameter at the call point. The first parameter of the call point is identified by _1, the second by _2, and so on. This could be used to specify parameters in different ways, reordering the parameters of a function call, etc. For example:

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 );

Finally, std::bind() could be used to bind a member function to the object used to call it:

struct foo
{
    void f() const;
};

int main()
{
    foo myfoo;
    std::function<void()> f = std::bind( &foo::f , std::cref( myfoo ) );

    f(); //Tah dah!
}

A member function could be viewed as a function with one hidden parameter, which is the object that the call is done with. Thats why the object is binded as first parameter.

But, exactly as in the examples above, if you only know certain number of parameters at the binding point, and need to specify others later at the call point, you should use placeholders:

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

Some details

The signature of the call object

Note that we use std::function to store the call object. The signature of that function depends on the type of the call object generated, that is, depends on the way you have specified the parameters at the binding point.

A call object is just another callable entity which acts as the call to the original function. Following with our f() function examples:

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

Here the signature of the call object is void(), because we have specified the hole set of parameters at the binding point, and no one is left to be specified at the call point (So the call object has no parameters).

In the case of a partial call:

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

f_call( 1 , 2 , 3 );

the signature of the call object is void(int,int,int), because we have left three parameters to be specified at the call point (Note the placeholders). In general the call object has the same number of parameters that placeholders you specified at the binding point..

Autres conseils

Note that the numbers of the placeholders specify the number of the parameter at the call point. The first parameter of the call point is identified by _1, the second by _2, and so on.

I want to add some examples for @ Manu343726 . If you try to transform a function with 3 parameters into a function with 2 parameters. You can specify 1 parameter at bind time and provide the rest 2 at call time.

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)

now 11 is x1 of add3, _1 is x2 of add3, _2 is x3 of add3. _2 means fn has 2 parameters to specify at call time. And this format is wrong:

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top