I have the following code to bind a member function to an instance of the class:
class Foo {
public:
int i;
void test() {
std::cout << i << std::endl;
}
};
int main() {
Foo f;
f.i = 100;
auto func = std::bind(&Foo::test, std::forward<Foo>(f));
f.i = 1000;
func();
}
But the std::bind
statement does not bind to f
by reference. Calling func
prints "100" instead of "1000" which is what I want.
However, If I change the statement to take a pointer it works.
auto func = std::bind(&Foo::test, &f);
But this is passing f
by pointer by my understanding, and as I thought std::bind
takes an r-value reference Arg&&
(as shown here) how can this work?
Can someone please explain this?
Arguments taken by
std::bind
are actually Universal references, which can bind to both lvalues and rvalues. You cannot just pass value tostd::bind
, since that will copy it.To pass the reference to the
std::bind
you can usestd::ref
:LIVE DEMO
Note on using
std::forward
First of all,
std::forward
is meant to be used for perfect forwarding, i.e. to forward the reference type (l-value or r-value).If you pass an l-value reference to
std::forward
that is what is returned, and likewise if an r-value reference is passed then an r-value is returned. This works as opposed tostd::move
that will always return an r-value reference. Also remember that named r-value references are l-value references.Also you should never use
std::forward
orstd::move
on an object that you afterwards need to access some state from. Objects that are moved from are put in an unspecified but valid state, which basically means that you cant do anything with them except destroy or reassign a state to them.Arguments passed by reference to
std::bind
?The function
std::bind
will always copy or move its arguments. I could not find an appropriate citation from the standard but en.cppreference.com says:This means that if you pass the argument(s) as an l-value reference then it is copy constructed, and if you pass it as an r-value reference then it is move constructed. In either way the argument(s) will never be passed as references.
To circumvent this you can e.g. use
std::ref
as a copyable reference wrapper that will internally keep a reference to the variable at the call site.Or you could simply pass a pointer to
f
as you suggest.What happens then is that
std::bind
will take an r-value reference to the temporary pointer returned from calling the address-of operator onf
. This pointer will be copied (as pointers can't be moved) into the bound wrapper object returned fromstd::bind
. Even though the pointer itself is copied, it will still point to the object at the call site and you will achieve the reference semantics you wanted.Alternatively use a lambda that captures
f
by reference and calls the functionFoo::test
. This is IMHO the recommended approach as lambdas are more versatile and powerful thanstd::bind
expressions in the general case.Note: for a good explanation of when to use
std::forward
see this excellent video by Scott Meyers.