我正在尝试使用一个 C++ 类(带有构造函数和析构函数),该类在
std::thread
对象中具有 std::vector
字段。我使用 C++ 17 和 Visual Studio 2022 编译下面所示的所有示例。
这是一个简单的最小示例,说明了我的问题:
#include <iostream>
#include <thread>
#include <vector>
class TEST
{
public:
std::thread t;
bool done;
TEST()
{
this->done = false;
this->t = std::thread(&TEST::foo, this);
}
~TEST()
{
this->done = true;
this->t.join();
}
void foo()
{
std::cout << "foo\n";
while (!this->done) {};
}
};
int main()
{
std::vector<TEST> vec = {};
vec.push_back(TEST());
vec.clear();
return 0;
}
我在
vec
函数中创建一个向量 main
,其中包含 TEST
类的对象,添加一个 TEST
对象,然后立即清除该向量。 TEST
类包含一个名为 std::thread
的 t
字段、一个名为 bool
的 done
字段以及默认的构造函数和析构函数。字段 t
中创建的每个线程仅调用 foo
函数,将 foo
打印到控制台,并等待直到析构函数将 done
字段设置为 true
,然后退出。析构函数只是加入线程。
上面例子编译时提示的具体错误如下:
Error C2280 'TEST::TEST(const TEST &)': attempting to reference a deleted function
据我了解,出现此错误的核心原因是
std::thread
类故意是不可复制的,因此它的复制构造函数在标准库中被显式删除。因此,当编译器尝试查找我的 TEST
类的默认复制构造函数时,它无法找到 std::thread
的复制构造函数,并且编译失败。
但是,我很难理解的是,只有当我添加默认析构函数
~TEST
时,才会出现此错误。删除它允许代码编译,尽管程序在运行时崩溃,我假设是因为 t
线程字段在对象销毁时没有正确退出。我不明白为什么这个错误只在我显式添加析构函数时才会出现,而不会在隐式空默认构造函数中出现。
我注意到的另一件事是,这段代码似乎可以在不使用
std::vector
的情况下编译和运行,没有问题,例如以下示例:
#include <iostream>
#include <thread>
class TEST
{
public:
std::thread t;
bool done;
TEST()
{
this->done = false;
this->t = std::thread(&TEST::foo, this);
}
~TEST()
{
this->done = true;
this->t.join();
}
void foo()
{
std::cout << "foo\n";
while (!this->done) {};
}
};
int main()
{
{
TEST test = TEST();
}
return 0;
}
我只是在
TEST
函数中创建一个独立的 main
对象,没有 std::vector
,并立即销毁它,编译和执行没有问题。看起来 TEST
中存在 std::vector
对象只会导致 ~TEST
析构函数无法编译。
我还尝试使用 emplace_back
的
std::vector
函数代替 push_back
,如 另一个帖子 所建议,出现相同的错误。
这是怎么回事?为什么
std::thread
中存在带有 std::vector
字段的对象会导致编译失败,而此类独立对象则不会?
当您声明类编译器的析构函数时,不会生成移动和复制构造函数。为了让
std::vector::emplace_back
发挥作用,你的班级至少必须是可移动的。要修复它,只需将这些声明添加到您的类中即可:
TEST(TEST&& other) = default;
TEST& operator=(TEST&& other) = default;