Graceful thread shutdown with std::move
volatile bool thread_shutdown = false;


This is definitely not correct. volatile is not for thread safety. What you want here is atomic&lt;bool&gt;.

Other than that, I'd say everything else looks technically kosher. The thing is, the semantics of your move operations will be weird now. For example, if I write:

auto func(A a) {}

auto a = A{};
func(std::move(a));


What I expect to happen is that a is cheaply moved into the function argument (and thus, into the function). Instead what happens here is that I've triggered a thread death plus the creation of a whole new thread. So much for moves being cheap - in fact, copying might be cheaper because it would create a new thread, but not destroy the old one.

If you want to define a constructor that steals data from another object, and creates an entirely new thread to use that data in, that's fine. But using the move constructor for that seems unwise. This is probably a job for a tagged constructor:

A() :
    b_ptr{std::make_unique&lt;B&gt;()}
{
    // I wouldn't rely on order of data members to guarantee the thread
    // is started after the data is set.
    t = std::thread{&amp;A::run, this};
}

A(steal_data_t, A&amp;&amp; a)
{
    a.shutdown_thread();
    b_ptr = std::move(a.b_ptr());
    t = std::thread{&amp;A::run, this};
}

A(A&amp;&amp;) noexcept = default; // well, you'd need to handle moving the atomic, I believe

// Usage:
auto a1 = A{};
auto a2 = std::move(a1); // this invalidates a1, as usual
auto a3 = A{steal_data, std::move(a2)}; // this invalidates a2, as expected


You could avoid some repetition with private helper functions.