I have a class with a thread object that is using class members.
class MyClass
{
private:
atomic_int a;
thread t;
public:
MyClass()
: a(0),
t([this]() // <- grab 'this' pointer
{
while(true)
{
sleep_for(milliseconds(1000));
a++; // <- use 'a' field of the object (through 'this')
}
})
{
}
};
Question: How to implement move assignment / constructor, so that the thread object references the new MyClass object?
Naive (bad) implementation:
class MyClass
{
public:
MyClass(MyClass&& other)
: a(other.a),
t(other.t) // <- Thread object is moved to the new MyClass object
// but it's still referencing the old 'this' pointer.
{
}
}
You can’t move an atomic
object. This is a hint: while you could write your own move constructor that initialized the new object’s a
with the value of the old one, the behavior wouldn’t be atomic! (You would also have to synchronize between threads to publish the address of the new atomic object.)
So don’t move it. That immediately implies that the atomic object must not be a (direct) member of your movable class; the next simplest thing (aside from making it local to the lambda, which is presumably undesirable) is to own it via unique_ptr
(which already has the desired movability-but-not-copyability). Then the lambda can capture the raw pointer to avoid having a reference to (a subobject of) this
that could dangle:
class MyClass
{
private:
unique_ptr<atomic_int> a;
thread t;
public:
MyClass()
: a(new atomic_int(0)),
t([a=a.get()]() // <- grab pointer to atomic
{
while(true)
{
sleep_for(milliseconds(1000));
++*a;
}
})
{
}
};
The default move constructor/assignment are then correct. (Note that assignment calls terminate
if the thread is still running, which makes sense since you just deallocated the atomic_int
it was using!)