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?
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 to std::move
that will always return an r-value reference. Also remember that named r-value references are l-value references.
/* Function that takes r-value reference. */
void func(my_type&& t) {
// 't' is named and thus is an l-value reference here.
// Pass 't' as lvalue reference.
some_other_func(t);
// Pass 't' as rvalue reference (forwards rvalueness).
some_other_func(std::forward<my_type>(t));
// 'std::move' should be used instead as we know 't' is always an rvalue.
// e.g. some_other_func(std::move(t));
}
Also you should never use std::forward
or std::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.
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:
"The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref."
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.
auto func = std::bind(&Foo::test, std::ref(f));
Or you could simply pass a pointer to f
as you suggest.
auto func = std::bind(&Foo::test, &f);
What happens then is that std::bind
will take an r-value reference to the temporary pointer returned from calling the address-of operator on f
. This pointer will be copied (as pointers can't be moved) into the bound wrapper object returned from std::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 function Foo::test
. This is IMHO the recommended approach as lambdas are more versatile and powerful than std::bind
expressions in the general case.
Foo f;
f.i = 100;
auto func = [&f] { f.test(); };
f.i = 1000;
func(); // 1000
Note: for a good explanation of when to use std::forward
see this excellent video by Scott Meyers.