Passing object by reference to stdthread in C11
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In C++11, std::thread copies arguments by default, which is safe but often surprising when you expect the worker to update the original object. Passing by reference requires explicit intent with std::ref or std::cref. Correctness also depends on object lifetime and synchronization, because reference passing does not automatically make shared access thread-safe.
Understand the Default Copy Semantics
When you write std::thread(fn, arg), the argument is stored as a decayed value inside thread state. That means your callable often receives a copy, even if arg is an lvalue in the caller.
The worker prints modified text, but value in main is unchanged because the thread function operated on a copy.
Pass Writable References with std::ref
To pass the original object, wrap it in std::ref. The callable should accept a reference parameter.
For read-only access, use std::cref with a const reference parameter.
Lifetime Rules Are Non-Negotiable
If you pass a reference, the referenced object must outlive the thread’s use of it. This is where many subtle crashes come from.
Safe pattern:
- allocate object in scope that survives thread execution.
- launch thread with
std::ref. - '
joinbefore leaving scope.'
Danger pattern:
- pass reference to stack object.
- detach thread.
- function returns while thread still reading or writing object.
Detached threads with references require very careful ownership design and are best avoided unless absolutely necessary.
Shared Mutation Needs Synchronization
Passing by reference only shares identity. It does not prevent races. If two threads write or one writes while another reads, protect with mutexes or atomics.
Without the mutex this program has undefined behavior.
Passing Class Instances and Member Functions
For member functions, pass the object pointer or reference plus method pointer. If the object should be shared and mutated, use std::ref on the instance.
This works because std::ref(a) ensures the member function runs on the original object.
References Versus Moves
Not all thread argument issues are about references. For move-only types such as std::unique_ptr, pass ownership with std::move, not std::ref.
Use reference when shared lifetime is external. Use move when the worker should own the resource.
Common Pitfalls
- Forgetting
std::refand expecting caller object to be modified. - Passing references to temporaries or short-lived stack values.
- Sharing mutable referenced state without mutexes or atomics.
- Detaching threads that still depend on referenced objects.
- Confusing reference passing with move semantics for ownership transfer.
Summary
- '
std::threadcopies arguments unless you explicitly request reference semantics.' - Use
std::reffor writable references andstd::creffor read-only references. - Ensure referenced objects outlive all thread use.
- Synchronize shared mutable access to avoid data races.
- Distinguish clearly between shared references and moved ownership.

