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
- cppreference - std::thread
- cppreference - std::bind
- cppreference - std::decay
- cppreference - std::reference_wrapper
- cppreference - std::ref