I'm attempting to use a C++ class (with constructors & destructors), that has an std::thread field, in an std::vector object. I'm using C++ 17 and Visual Studio 2022 to compile all examples shown below.
Here is a simple minimal example that illustrates my issue:
#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;
}
Where I create a vector vec in the main function that contains objects of TEST class, add one TEST object, and then immediately clear the vector. The TEST class contains an std::thread field called t, a bool field called done and a default constructor and destructor. Each created thread in field t just calls the foo function, prints foo to the console, and waits until the done field is set to true by the destructor, and exits. The destructor simply joins the thread.
The specific error I'm prompted with upon a compilation attempt of the above example is as follows:
Error C2280 'TEST::TEST(const TEST &)': attempting to reference a deleted function
It is my understanding that the core reason for the appearance of this error is the fact that the std::thread class is intentionally non-copyable, and therefore it's copy constructor was explicitly deleted in the standard library. Hence, when the compiler attempts to find the default copy constructor of my TEST class, it is unable to find std::threads copy constructor, and the compilation fails.
However, what I'm struggling to understand is the fact that this error only appears to occur when the default destructor ~TEST is added by me. Removing it allows the code to compile, albeit the program crashes when ran, I'm assuming because the t thread field doesn't exit properly upon the object's destruction. I don’t understand the reason as to why this error only manifests when the destructor is explicitly added by me, and doesn’t appear with the implicit empty default constructor.
Another thing I noticed is the fact that this code appears to compile & run without issues without the std::vector being used, for instance the following example:
#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;
}
Where I just create a single standalone TEST object in the main function, without an std::vector, and immediately destroy it compiles & executes without issues. It appears that the presence of TEST objects in an std::vector simply causes the ~TEST destructor to fail to compile.
I've also tried using the emplace_back function of std::vector instead of push_back, as suggested by another posting, with the same error appearing.
What is going on here? Why is the presence of objects with std::thread fields in an std::vector causing compilation failures, when such standalone objects do not?
TESTshould support. Should we be able to copy that? No. To move that? Well... still no? If we allowTESTto be moved, thethispointer captured by the thread of one object points to another object, and if the latter gets deleted... UB. Without being able to copy or to (noexcept) move,std::vectorcan not work. This class needs to be rethinked, or the thread needs to way to update the captured pointer when the underlyingTESTobject gets moved.