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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With