Как дождаться создания объекта в другом потоке для вызова деструктора?

Необходимо иметь возможность создать объект в другом потоке и затем удалить в любое время (для асинхронного создания и перехода в машине состояний). Вызывается конструктор в другом потоке, в это время основной поток пытается вызвать деструктор, но до создания объект не удалятся. Этот код, к сожалению, не работает. Деструктор вовсе не вызывается и Visual Leak Detector находит утечку памяти:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>

class Ex
{
private:

    std::mutex m_mutex;
    std::string m_str;

public:

    Ex(const std::string& str) :
        m_str(str)
    {
        std::lock_guard lock(m_mutex);
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << "Ex constructed!\n";
    }

    const std::string& get() const
    {
        return m_str;
    }

    ~Ex()
    {
        std::lock_guard lock(m_mutex);
        std::cout << "Ex destructed!" << " m_str = " << m_str << '\n';
    }
};

int main()
{
    {
        std::unique_ptr<Ex> ptr;
        std::thread thread([&]
            {
                ptr = std::make_unique<Ex>("string");
            });
        thread.detach();
    }

    std::getchar();
}

Ответы (3 шт):

Автор решения: ManyBytes

Спасибо за помощь! Было наивно полагать, что мьютекс объекта умного указателя будет распространятся на сам умный указатель. Если я правильно понял, std::shared_ptr использует атомарный подсчёт ссылок и будет правильно удалён в зависимости от того, какой поток последним вызовет его деструктор:

std::shared_ptr<Ex> ptr;
std::thread thread([](std::shared_ptr<Ex> p)
    {
        p = std::make_shared<Ex>("string");
    }, ptr);
thread.detach();

Теперь единственная ошибка, которая может произойти - при выходе из программы отсоединённый поток будет просто уничтожен и ресурсы не будут правильно освобождены.

PS: Если кто-то набрёл на этот ответ: он неверный.

→ Ссылка
Автор решения: ManyBytes

Разве этот код:

std::shared_ptr<Ex> ptr = std::make_shared<Ex>("string");
stateMachine.append(ptr);

Не будет работать аналогичным образом с этим?

    std::shared_ptr<Ex> ptr;
    std::shared_ptr <std::atomic<bool>> flag = std::make_shared<std::atomic<bool>>(false);
    std::thread thread([&](std::shared_ptr<Ex> p, std::shared_ptr <std::atomic<bool>>)
        {
            p = std::make_shared<Ex>("string");
            flag->store(true);
        }, ptr, flag);
    thread.detach();

    while (!flag->load())
    {
    }

    stateMachine.append(ptr);

PS: я проверил - действительно не работает. Потому что я перезаписываю умный указатель. Спасибо за помощь - буду уже самостоятельно подробно изучать как всё это работает.

→ Ссылка
Автор решения: ManyBytes

Теперь я наконец-то понял, зачем использовать std::future и возвращать значения из потока. Думаю, это идеальное решение, ведь при "внезапном" уничтожении срабатывает функция wait(), то есть главный поток гарантированно дождётся побочного. К тому же, нет возни с исключениями в потоке:

int main()
{
    std::shared_ptr<Ex> ptr;
    auto future = std::async(std::launch::async,
        []
        {
            return std::make_shared<Ex>("Hello world!");
        });
    ptr = future.get();
    std::getchar();
}
→ Ссылка