具有模板化构造函数的 C++ 自定义线程包装器导致插入 std::map 时出现编译时错误

C++ Custom Thread Wrapper with Templatized Constructor causes compile time error for insertion into std::map

我正在编写自己的基本游戏引擎,并且正在对其进行多线程处理。我创建了一个 RawThread 对象,它用消息队列等包装了一个 std::thread。我想将这些 RawThread 映射到一个 id(unsigned int)。所以我尝试将一个插入到 std::map<unsigned int, RawThread> 中,但我得到了一堆非常模棱两可的模板错误。如果我注释掉 map.insert 行,那么代码编译得很好,一切正常,但我希望能够将 RawThread 插入地图,如 main() 中所尝试的那样。

我盯着这段代码看了一个小时,检查我的 RawThread 构造函数模板是否有误,或者我是否错误地使用了 references/pointer,但我看不出问题所在.

#include <map>
#include <utility>
#include <thread>

class RawThread
{
public:

    RawThread(const RawThread& you)
    {
        *this = you;
    }



    ~RawThread()
    {
        delete mThread;
    }

    template<class Function, class... Args>
    RawThread(Function&&f, Args&&... args)
    {       
        mThread = new std::thread(std::forward<Function>(f),         
                                          std::forward<Args>(args)...);
    }

    const RawThread& operator=(const RawThread& in)
    {
        this->mThread = in.mThread;
        return *this;
    }

    void join()
    {
        mThread->join();
    }
    std::thread* mThread;
};



void hello(int x){while(true)++x;}

int main()
{
    int arg = 0;
    RawThread myThread(&hello, arg);
    unsigned int threadId = 0;
    std::map<unsigned int, RawThread> threadMap;
    threadMap.insert(std::make_pair(threadId, myThread));
    return 0;
}

我遇到了一个看起来很讨厌的错误,所以我把它放在了 pastebin 上:https://pastebin.com/9A4wN7kL

#include <map>
#include <utility>
#include <thread>
#include <memory>

class RawThread
{
public:

    template<class Function, class... Args>
    RawThread(Function&&f, Args&&... args)
    {
        mThread = std::make_shared<std::thread>(std::forward<Function>(f),
                                                std::forward<Args>(args)...);
    }
    RawThread(const RawThread &)=default;
    RawThread(RawThread &&)=default;
    RawThread()=delete;
    void join()
    {
        mThread->join();
    }

    std::shared_ptr<std::thread> mThread;
};



void hello(int x){while(true)++x;}

int main()
{
    int arg = 0;
    RawThread myThread(&hello, arg);
    unsigned int threadId = 0;
    std::map<unsigned int, RawThread> threadMap;
    //This line is wrong because it uses the constructor template instead of the copy constructor
    //threadMap.insert(std::make_pair(threadId, myThread);
    threadMap.insert(std::make_pair(threadId, (const RawThread &)myThread));//Good, it uses the copy constructor
    threadMap.insert(std::make_pair(threadId, std::move(myThread)));//Good, it uses the move constructor
    return 0;
}

insert 的问题是 RawThread 的副本是通过调用

创建的
template<class Function, class... Args>
RawThread(Function&&f, Args&&... args)

那么 FunctionRawThread 的实例,而 Args 是空的。 std::thread 构造函数可以将对象的实例作为其第一个参数,但是此实例的 class 必须提供 operator()() 作为线程的主体。您的 class 缺少它。您可以添加它,代码将编译。

class RawThread {
    //....
    void operator()() {}
};

但这不是你想要的。


对我来说,您应该以 class 的只有一个实例保持活动 mThread 指针的方式实现 RawThreadRawThread 应该只移动,复制应该被禁用。如果许多实例共享相同的 mThread 值,应该调用哪个对象加入或删除该对象?

只支持移动的版本可能是:

#include <map>
#include <utility>
#include <thread>

class RawThread {
public:
    ~RawThread()
    {
        if (mThread)
        {
            if (mThread->joinable())
                mThread->join();
            delete mThread;
        }
    }

    RawThread(RawThread&& theOther) {
        mThread = theOther.mThread;
        theOther.mThread = nullptr;
    }

    RawThread& operator=(RawThread&& theOther) {
        mThread = theOther.mThread;
        theOther.mThread = nullptr;
        return *this;        
    }

    template<class Function, class... Args>
    RawThread(Function&&f, Args&&... args) {
        mThread = new std::thread(std::forward<Function>(f),         
                                  std::forward<Args>(args)...);
    }

    void join() {
        mThread->join();
    }
    std::thread* mThread;
};

void hello(int x){while(true)++x;}

int main()
{
    int arg = 0;
    RawThread myThread(&hello, arg);
    unsigned int threadId = 0;
    std::map<unsigned int, RawThread> threadMap;
    threadMap.emplace(threadId,std::move(myThread)); // move object into map
    return 0;
}