Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind function to an object by reference?

Tags:

c++

stdbind

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?

like image 342
SwiftMango Avatar asked Oct 03 '14 21:10

SwiftMango


People also ask

How do you bind an object in JavaScript?

We use the Bind() method to call a function with the this value, this keyword refers to the same object which is currently selected . In other words, bind() method allows us to easily set which object will be bound by the this keyword when a function or method is invoked.

What does bind () method of?

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

Does JavaScript pass by reference or value?

JavaScript pass-by-value or pass-by-reference In JavaScript, all function arguments are always passed by value. It means that JavaScript copies the values of the variables into the function arguments.


2 Answers

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 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.

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:

"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.

like image 110
Felix Glas Avatar answered Oct 17 '22 20:10

Felix Glas


Arguments taken by std::bind are actually Universal references, which can bind to both lvalues and rvalues. You cannot just pass value to std::bind, since that will copy it.

To pass the reference to the std::bind you can use std::ref:

auto func = std::bind(&Foo::test, std::ref(f));

LIVE DEMO

like image 6
Rafal Mielniczuk Avatar answered Oct 17 '22 19:10

Rafal Mielniczuk