std::ref
makes a std::reference_wrapper
of the appropriate type.
Reference wrappers are pointers to the referred to thing and act as value types, but also implicitly convert to (or be explicitly converted to) references.
So a reference wrapper can be stored in a vector.
In your case, std thread is storing the parameters in something tuple like and thrn invoking it on another thread. Because it is definitely unsafe to capture the parameters by reference when the invoking could occur long after local scope is dead, the parameters are captured by value. In addition, because the values are discarded after the thread starts, they are passed to the thread function as rvalues not as lvalues.
Rvalues are stuff safe to read from but no guarantee they stick around. Lvalues are stuff it makes sense to assign to, and assigning to something that is being discarded is usually nonsense. L and R here come from left and right sides of the assignment operator (Left = Right) from C.
So
std::thread worker1(thread_executor, X);
here, X
is copied into a temporary tuple, then the copy is passed to thread_exrcutor
as a rvalue. Rvalues of type int
cannot bind to int&
(aka an lvalue reference to int
) so the compiler rejects your code.
When you create a std::reference_wrapper<int>
and pass it in, rvalues of that type can convert to int&
. So the code is accepted.
std::ref(X)
is equivalent to std::reference_wrapper<int>(X)
; it just deduces int
for you.
std::cref
just injects a const
; sts::cref(X)
is std::reference_wrapper<int const>(X)
.
By using std::ref
you promise to manage lifetime properly here. The language and library was designed to make the lifetime errors you'd get without it less common.