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? Hasstd::bind
some 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 useyou 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
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 typedouble(*)(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 typevoid (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 usestd::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 tostd::bind()
correspondingly needs to specify when the object is coming from. In your examplethe resulting callable object uses
&foo
, i.e., a pointer tofoo
(of typeFoo*
) 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 (likestd::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:
The function
apply()
simply gets two pointers toFoo
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 memberf()
would be called on the respective objects. Likewise when callingapply()
with the two pointers and&Foo::g
it behaves exactly the same as if the memberg()
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 aCallable
, in your case that is a pointer to member function. This kind of pointers has some special syntax compared to pointers to usual functions:std::bind
(andstd::invoke
in general) covers all these cases in a uniform way. Iff
is a pointer-to-member ofFoo
, then the firstArg
provided to bind is expected to be an instance ofFoo
(bind(&Foo::print_sum, foo, ...)
also works, butfoo
is copied) or a pointer toFoo
, 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: