I'm working with std::bind
but I still don't get how it works when we use it with member class functions.
If we have the following function:
double my_divide (double x, double y) {return x/y;}
I understand perfectly well the next lines of code:
auto fn_half = std::bind (my_divide,_1,2); // returns x/2 std::cout << fn_half(10) << '\n'; // 5
But now, with the following code where we have a bind to member function I have some questions.
struct Foo { void print_sum(int n1, int n2) { std::cout << n1+n2 << '\n'; } int data = 10; }; Foo foo; auto f = std::bind(&Foo::print_sum, &foo, 95, _1); f(5);
Why is the first argument a reference? I'd like to get a theoretical explanation.
The second argument is a reference to the object and it's for me the most complicated part to understand. I think it's because std::bind
needs a context, am I right? Is always like this? Has std::bind
some sort of implementation to require a reference when the first argument is a member function?
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.
How does bind() work? Bind function with the help of placeholders helps to manipulate the position and number of values to be used by the function and modifies the function according to the desired output. What are placeholders? Placeholders are namespaces that direct the position of a value in a function.
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.
boost::bind is a generalization of the standard functions std::bind1st and std::bind2nd. It supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions.
When you say "the first argument is a reference" you surely meant to say "the first argument is a pointer": the &
operator takes the address of an object, yielding a pointer.
Before answering this question, let's briefly step back and look at your first use of std::bind()
when you use
std::bind(my_divide, 2, 2)
you provide a function. When a function is passed anywhere it decays into a pointer. The above expression is equivalent to this one, explicitly taking the address
std::bind(&my_divide, 2, 2)
The first argument to std::bind()
is an object identifying how to call a function. In the above case it is a pointer to function with type double(*)(double, double)
. Any other callable object with a suitable function call operator would do, too.
Since member functions are quite common, std::bind()
provides support for dealing with pointer to member functions. When you use &print_sum
you just get a pointer to a member function, i.e., an entity of type void (Foo::*)(int, int)
. While function names implicitly decay to pointers to functions, i.e., the &
can be omitted, the same is not true for member functions (or data members, for that matter): to get a pointer to a member function it is necessary to use the &
.
Note that a pointer to member is specific to a class
but it can be used with any object that class. That is, it is independent of any particular object. C++ doesn't have a direct way to get a member function directly bound to an object (I think in C# you can obtain functions directly bound to an object by using an object with an applied member name; however, it is 10+ years since I last programmed a bit of C#).
Internally, std::bind()
detects that a pointer to a member function is passed and most likely turns it into a callable objects, e.g., by use std::mem_fn()
with its first argument. Since a non-static
member function needs an object, the first argument to the resolution callable object is either a reference or a [smart] pointer to an object of the appropriate class.
To use a pointer to member function an object is needed. When using a pointer to member with std::bind()
the second argument to std::bind()
correspondingly needs to specify when the object is coming from. In your example
std::bind(&Foo::print_sum, &foo, 95, _1)
the resulting callable object uses &foo
, i.e., a pointer to foo
(of type Foo*
) as the object. std::bind()
is smart enough to use anything which looks like a pointer, anything convertible to a reference of the appropriate type (like std::reference_wrapper<Foo>
), or a [copy] of an object as the object when the first argument is a pointer to member.
I suspect, you have never seen a pointer to member - otherwise it would be quite clear. Here is a simple example:
#include <iostream> struct Foo { int value; void f() { std::cout << "f(" << this->value << ")\n"; } void g() { std::cout << "g(" << this->value << ")\n"; } }; void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) { (foo1->*fun)(); // call fun on the object foo1 (foo2->*fun)(); // call fun on the object foo2 } int main() { Foo foo1{1}; Foo foo2{2}; apply(&foo1, &foo2, &Foo::f); apply(&foo1, &foo2, &Foo::g); }
The function apply()
simply gets two pointers to Foo
objects and a pointer to a member function. It calls the member function pointed to with each of the objects. This funny ->*
operator is applying a pointer to a member to a pointer to an object. There is also a .*
operator which applies a pointer to a member to an object (or, as they behave just like objects, a reference to an object). Since a pointer to a member function needs an object, it is necessary to use this operator which asks for an object. Internally, std::bind()
arranges the same to happen.
When apply()
is called with the two pointers and &Foo::f
it behaves exactly the same as if the member f()
would be called on the respective objects. Likewise when calling apply()
with the two pointers and &Foo::g
it behaves exactly the same as if the member g()
would be called on the respective objects (the semantic behavior is the same but the compiler is likely to have a much harder time inlining functions and typically fails doing so when pointers to members are involved).
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