Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is std::bind not working without placeholders in this example (member function)?

Tags:

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?

like image 787
Sadeq Avatar asked Mar 15 '14 09:03

Sadeq


People also ask

What are std :: placeholders?

std::placeholdersThis namespace declares an unspecified number of objects: _1 , _2 , _3 ,..., which are used to specify placeholders in calls to function bind .

What does std :: bind do in C++?

std::bind is a Standard Function Objects that acts as a Functional Adaptor i.e. it takes a function as input and returns a new function Object as an output with with one or more of the arguments of passed function bound or rearranged.

How does STD bind work?

std::bind allows you to create a std::function object that acts as a wrapper for the target function (or Callable object). std::bind also allows you to keep specific arguments at fixed values while leaving other arguments variable.

Is std :: bind deprecated?

Yes: std::bind should be replaced by lambda For almost all cases, std::bind should be replaced by a lambda expression. It's idiomatic, and results in better code. There is almost no reason post C++11 to use std::bind .


1 Answers

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..

like image 67
Manu343726 Avatar answered Oct 19 '22 07:10

Manu343726