Passing reference with std::ref in C++

When you first learn C++, you probably know about reference. It allows you to pass parameter as if you are passing by value while having a similar effect as passing a pointer. Sometimes, however, this may not work as you expect. In this post, I will discuss how to pass a reference when the normal way doesn’t work.

Passing parameters to std::thread and std::bind

If you have used std::thread or std::bind, you probably noticed that even if you pass a reference as parameter, it still creates a copy instead.

#include <functional>
#include <iostream>

using namespace std;

void inc(int& x)
{
        x++;
        cout << "Inside inc, x is now " << x << endl;
}

int main()
{
        int a = 0;

        auto func1 = bind(inc, a);
        func1();
        cout << "After func1, value of a is " << a << endl;
}
Inside inc, x is now 1
After func1, value of a is 0

From cppreference,

The arguments to the thread function are moved or copied by value.

How is this done? The answer is using std::decay. Again from cppreference,

Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type.

Simply speaking, std::decay can remove references from type. For example, std::decay<int&>::type is the same as just int. For std::thread and std::bind, when they store the parameters you passed in, std::decay<T>::type is used as the type for each of the parameters. Therefore, when a reference is passed in, a copy is created instead of simply storing a reference.

#include <iostream>
#include <type_traits>

using namespace std;

int main()
{
        int a = 0;
        int& a_ref = a;
        decay<int&>::type a_copy = a_ref;

        a_ref++;
        cout << a << endl;
        cout << a_copy << endl;
}
1
0

std::ref to the rescue

To overcome what std::decay does to references, we must have a copiable object which stores a reference internally. This is exactly what std::reference_wrapper does.

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

std::ref is a handy function in the standard library to create a std::reference_wrapper object. With std::ref, we can finally pass references as parameters correctly as shown below (continuing the example above).

int main()
{
        int a = 0;
        int b = 0;

        auto func1 = bind(inc, a);
        func1();
        cout << "After func1, value of a is " << a << endl;

        auto func2 = bind(inc, ref(b));
        func2();
        cout << "After func2, value of b is " << b << endl;
}
Inside inc, x is now 1
After func1, value of a is 0
Inside inc, x is now 1
After func2, value of b is 1

Conclusion

In this post, I described the std::ref function, and why we need to use it in order to pass references to things like std::thread or std::bind. I hope you now have a better understanding of what std::ref is for in C++.

References

 
comments powered by Disqus