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 the first argument is 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::bindneeds a context, am I right? Is always like this? Hasstd::bindsome sort of implementation to require a reference when the first argument is a member function?
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).
From std::bind docs:
bind( F&& f, Args&&... args ); where f is a Callable, in your case that is a pointer to member function. This kind of pointers has some special syntax compared to pointers to usual functions:
typedef void (Foo::*FooMemberPtr)(int, int);
// obtain the pointer to a member function
FooMemberPtr a = &Foo::print_sum; //instead of just a = my_divide
// use it
(foo.*a)(1, 2) //instead of a(1, 2)
std::bind(and std::invoke in general) covers all these cases in a uniform way. If f is a pointer-to-member of Foo, then the first Arg provided to bind is expected to be an instance of Foo (bind(&Foo::print_sum, foo, ...) also works, but foo is copied) or a pointer to Foo, like in example you had.
Here is some more reading about pointers to members, and 1 and 2 gives full information about what bind expects and how it invokes stored function.
You also can use lambdas instead std::bind, which could be more clear:
auto f = [&](int n) { return foo.print_sum(95, n); }
来源:https://stackoverflow.com/questions/37636373/how-stdbind-works-with-member-functions