c++multithreadingshared-ptrmove-constructorenable-shared-from-this

does the move constructor invalidate shared_from_this


I would like to start a thread with a class which contains non-copyable members. In order to communicate with the thread I would like to create a shared pointer from the object before I move it into the thread.

Does the move constructor invalidate the shared pointer? If so, what is the elegant c++ way of doing this? One solution would be to wrap MessageClient into a shared pointer, but that hacks around the move constructor.

class MessageClient : public std::enable_shared_from_this<MessageClient> {
private:
    boost::asio::io_context io_context;

    void send() {
      // code
    }

    void operator() () {
      // code
    }
};

int main () {
   MessageClient client;
   auto pclient = client.shared_from_this();
   std::thread thread(std::move(client));

   pclient->send(); // terminate called after throwing an instance of  'std::bad_weak_ptr'
                    // what():  bad_weak_ptr
                    // Aborted (core dumped)
   thread.join();
}

EDIT

I got my answer. I understand now that I have used the enable_shared_from_this incorrectly, but real answer is, that there is no way of solving the issue which does not involve wrapping the object into another object, such as smart pointer or lambda function (which is wrapping the object into a functor). Personally I have found the second solution simpler, that is why I have selected it.

MessageClient client;
std::thread thread([&client](){client.run();});

client.send();

EDIT2

I have found a more obvious solution. If I create a reference from the object I do not have to wrap it:

MessageClient client;
std::thread thread(std::ref(client));

client.send();

Solution

  • Deriving from enable_shared_from_this hints that every instance is owned by some shared_ptrs. Your problem is that client isn't, and is unrelated to moving it into the thread.

    You have undefined behaviour on this line:

    auto pclient = client.shared_from_this();
    

    Just use a std::shared_ptr<MessageClient> from the start.

    class MessageClient {
    private:
        boost::asio::io_context io_context;
    
        void send() {
          // code
        }
    
        void operator() () {
          // code
        }
    };
    
    int main () {
       auto pclient = std::make_shared<MessageClient>();
       std::thread thread([pclient]() { (*pclient)(); });
    
       pclient->send(); 
    
       thread.join();
    }