Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bind makes no sense to me whatsoever

The following:

#include <functional>

struct Foo 
{
    void bar1() {}
    void bar2(int) {}
    void bar3(int, int) {}
    void bar4(int, int, int) {}
};


int main()
{
    Foo foo;

    auto f1 = std::bind(&Foo::bar1, &foo);
    auto f2 = std::bind(&Foo::bar2, &foo);
    auto f3 = std::bind(&Foo::bar3, &foo);
    auto f4 = std::bind(&Foo::bar4, &foo);

    f1(1, 2, 3, 4); // success
    f2(1, 2, 3);    // error
    f3(1, 2);       // error
    f4(1);          // error    

    return 0;
}

f1(1, 2, 3, 4) compiles and executes bar1(). f2(1, 2, 3), doesn't compile, f3(1, 2) doesn't compile (yet bar3 has the correct prototype) and f4(1) doesn't compile. The error I get with Visual Studio 2013 for those 3 cases is

"class does not define an 'operator()' or a user defined conversion operator to a pointer-to-function or reference-to-function that takes appropriate number of arguments"

I have limited understanding of templates and the Standard Library, but this doesn't seem to make any logical sense to me. Is there a reasonably simple explanation?

like image 484
Robinson Avatar asked Apr 13 '15 15:04

Robinson


People also ask

What does std :: bind do?

std::bind. 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.

Does STD bind allocate?

As far as I can tell std::bind just concatenates stuff together into an object, so binding anything to a member function will result in an object that is at least three pointers in size. Assigning this object to a std::function will result in dynamic memory allocation.

What is the return type of std :: bind?

std::bind. Returns a function object based on fn , but with its arguments bound to args . Each argument may either be bound to a value or be a placeholder: - If bound to a value, calling the returned function object will always use that value as argument.


1 Answers

To pass arguments to the target you need to provide those arguments in the bind expression, or leave them unbound by adding placeholders to the bind expression and then you must call the function with arguments to be substituted for the placeholders.

You can call bar1 without placeholders because it doesn't take any arguments, so you don't need to pass anything to it. The arguments you pass to f1 are just ignored, because there are no unbound arguments, i.e. no placeholders that need replacing.

The other functions require arguments, so you must either bind arguments at "bind time" e.g.

auto f2a = std::bind(&Foo::bar2, &foo, 1);
f2a();

or leave the arguments unbound and provide them when you invoke the callable object:

auto f2b = std::bind(&Foo::bar2, &foo, std::placeholders::_1);
f2b(1);

Note that GCC 5 now has static assertions to catch this kind of error. If the arity of the target function can be determined and the bind expression doesn't have either a bound argument or placeholder for every parameter of the target function then it says:

/usr/local/gcc-head/include/c++/5.0.0/functional:1426:7: error: static assertion failed: Wrong number of arguments for pointer-to-member

What you wrote is equivalent to this, using lambdas instead of bind:

Foo foo;

auto f1 = [&foo](...) { return foo.bar1(); };
auto f2 = [&foo](...) { return foo.bar2(); };
auto f3 = [&foo](...) { return foo.bar3(); };
auto f4 = [&foo](...) { return foo.bar4(); };

f1(1, 2, 3, 4); // success
f2(1, 2, 3);    // error
f3(1, 2);       // error
f4(1);          // error   

i.e. you define functors that will take any arguments, but ignore them, and then call member functions of Foo, but not necessarily with the right arguments.

like image 186
Jonathan Wakely Avatar answered Sep 29 '22 17:09

Jonathan Wakely