如何用不可复制不可移动类的成员函数启动线程?VS2015编译报错疑问
问题1:如何使用不可复制且不可移动类的对象成员函数启动线程?
这个问题挺常见的——当类因为包含std::mutex、std::unique_ptr这类不可复制移动的成员,导致自身也不可复制、不可移动时,直接把对象传递给std::thread构造函数肯定会失败,因为默认情况下std::thread会尝试复制或移动对象。这里有两种可靠的解决办法:
传递对象的指针:直接把对象的地址传给
std::thread,这样不需要复制或移动对象,线程会通过指针调用成员函数。示例代码如下:class NonCopyableNonMovable { public: NonCopyableNonMovable() = default; // 显式删除复制、移动构造和赋值运算符 NonCopyableNonMovable(const NonCopyableNonMovable&) = delete; NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete; NonCopyableNonMovable(NonCopyableNonMovable&&) = delete; NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete; void do_work() { // 线程执行的逻辑 } }; int main() { NonCopyableNonMovable obj; std::thread t(&NonCopyableNonMovable::do_work, &obj); // 传递对象指针 t.join(); return 0; }用
std::ref传递对象的引用:通过std::reference_wrapper(std::ref会帮你生成这个包装器)让std::thread以引用方式持有对象,避免复制/移动。一定要注意:必须保证对象的生命周期长于线程,否则会出现悬空引用,导致未定义行为。示例代码:#include <functional> // 必须包含这个头文件才能用std::ref // 类定义同上... int main() { NonCopyableNonMovable obj; std::thread t(&NonCopyableNonMovable::do_work, std::ref(obj)); // 传递引用 t.join(); return 0; }
问题2:代码在Visual Studio 2015中无法编译,但在ideone上可正常运行的原因?
先看你给出的代码:
class FooMutex { std::mutex stm; public: void foo() {} }; int main() { FooMutex o; std::thread t1(&FooMutex::foo, o); }
核心原因
FooMutex的可复制性问题:
std::mutex是C++标准中明确规定的不可复制、不可移动类型,所以当你的FooMutex类包含std::mutex成员时,编译器会自动删除FooMutex的默认复制构造函数和移动构造函数——也就是说,FooMutex对象是不能被复制或移动的。标准库实现的版本差异:
- Visual Studio 2015的缺陷:VS2015附带的标准库实现存在不足,当你把
o(左值对象)传递给std::thread构造函数,配合成员函数指针使用时,它错误地尝试复制o对象。但因为FooMutex不可复制,所以触发了C2664的编译错误(错误信息里的参数匹配失败本质就是复制操作无法完成)。 - ideone的编译器(通常是GCC较新版本):更新的GCC标准库完全遵循了C++11及之后的标准,正确处理了这种场景——它不会尝试复制不可复制的对象,而是通过引用传递的方式(或者其他符合标准的机制)来构造线程,所以代码可以正常编译运行。
- Visual Studio 2015的缺陷:VS2015附带的标准库实现存在不足,当你把
修复VS2015的编译错误
只需要调整传递o的方式,和问题1的解决思路一致:
// 方式1:传递对象指针 std::thread t1(&FooMutex::foo, &o); // 方式2:用std::ref传递引用(记得包含<functional>头文件) std::thread t1(&FooMutex::foo, std::ref(o));
内容的提问来源于stack exchange,提问作者Daemon
相关产品推荐
相关产品推荐

