I have a thread that I want to sit in a loop until I'm ready to exit the program, at which point I want it to break out of the loop and exit so I can call std::thread::join
on it. In the days of c++03, I would just use a bool protected by a lock in order to tell the thread when to exit. This time I thought I would take advantage of the new atomics library (specifically std::atomic_bool
), but I'm having trouble. Below is my test case:
#include <atomic>
#include <thread>
#include <cstdio>
using namespace std;
void setBool(atomic_bool& ab)
{
ab = true;
}
int main()
{
atomic_bool b;
b = false;
thread t(setBool, b);
t.join();
printf("Atomic bool value: %d\n", b.load());
return 0;
}
The declaration of thread t
spits out this monstrosity when I try to compile. The central part of the error seems to be:
invalid initialization of non-const reference of type ‘std::atomic_bool&’ from an rvalue of type ‘std::atomic_bool’
Why can I not get a reference to an atomic_bool
? What should I do instead?
You have to explicitly pass a ref to your thread. Using std::ref
will create a std::reference_wrapper
which is copyable and will carry the ref to your function.
thread t(setBool, std::ref(b));
Otherwise it will try to copy your atomic, which is uncopyable.
As bamboon explained, you have to wrap objects with std::ref
if you really want them to be passed by reference via std::thread
's variable-argument constructor. (The same applies to std::async
.) To avoid this counterintuitive behavior, you can use a lambda, which behaves precisely as you would expect. Just use this to create the thread:
thread t([&]{ setBool(b); });
With lambdas, there is no need for the ref/cref nonsense when you want to pass arguments by reference.
I just ran into this as well, I think you could also solve your problem by passing an address-to the atomic like so:
std::atomic<bool> b{false};
std::thread t(setBool, &b);
Where your function takes a pointer to the atomic:
void setBool(std::atomic<bool>* ab)
I'm not sure what the accepted practice is, I suppose you could pass a null pointer to the atomic this way but I'm not sure why you would want to.