25

My aim is to keep an std::thread object as data member, and initialize it when needed.
I'm not able to do this (as in my code below) because the copy constructor of the std::thread class is deleted. Is there any other way to do it?

class MyClass
{
    public:
        MyClass():DiskJobThread(){};
        ~MyClass();

        void DoDiskJobThread();

    private:
        int CopyThread(const std::wstring & Source, const std::wstring & Target);
        int MoveThread(const std::wstring & Source, const std::wstring & Target);
        std::thread DiskJobThread;
};

MyClass::~MyClass()
{
    DiskJobThread.join();
}

void MyClass::DoDiskJobThread()
{
    std::wstring Source = GetSource();
    std::wstring Target = GetTarget();
    int m_OperationType = GetOperationType();
    if      (m_OperationType == OPERATION_COPY)
    {
        DiskJobThread = std::thread(&MyClass::CopyThread, *this, Source, Target);
    }
    else if (m_OperationType == OPERATION_MOVE)
    {
        DiskJobThread = std::thread(&MyClass::MoveThread, *this, Source, Target);
    }
}
hkBattousai
  • 10,583
  • 18
  • 76
  • 124

4 Answers4

18

How about wrapping it in a pointer?

std::unique_ptr<std::thread> thread_ptr;

// Look into std::make_unique if possible
thread_ptr = std::unique_ptr<std::thread>(new std::thread(...));

Edit: And yes, the others have mentioned it and I didn't feel the need to add it here, but in order to avoid more downvote piling, I'll say it: You are passing *this and not this thereby copying an instance of your class. (Problems arise because it's non-copyable. Pass this and you should be good to go.)

user123
  • 8,970
  • 2
  • 31
  • 52
  • 1
    Now `thread_ptr` is no more copyable and no less movable than the original `DiskJobThread`, though... – Kerrek SB Aug 22 '13 at 09:11
  • 1
    @KerrekSB Yes, but if the goal is only to initialize later, I _think_ it shouldn't be a problem ;o – user123 Aug 22 '13 at 09:15
  • 1
    -1: The problem is not with initialising `std::thread` (as that uses a move assignment op which is fine), so this does not answer the question. – Angew is no longer proud of SO Aug 22 '13 at 09:20
  • @Angew Okay, I have updated the answer to at least _mention_ the second part of the problem. – user123 Aug 22 '13 at 10:21
  • 1
    @MohammadAliBaydoun The problem with your answer is that it's not needed at all - storing `std::thread` by value is perfectly fine and it can indeed be initialised later, just like the OP is doing it. Storing it in a `unique_ptr` will just introduce inefficiencies through dynamic allocation/deallocation and indirection. – Angew is no longer proud of SO Aug 22 '13 at 10:50
  • @Angew I see that now. – user123 Aug 22 '13 at 11:41
12

Your problem is something else - you're passing an instance of MyClass into the thread instead of the pointer to MyClass which the member functions expect. Simply change DoDiskJobThread() like this (do not dereference this):

void MyClass::DoDiskJobThread()
{
    std::wstring Source = GetSource();
    std::wstring Target = GetTarget();
    int m_OperationType = GetOperationType();
    if      (m_OperationType == OPERATION_COPY)
    {
        DiskJobThread = std::thread(&MyClass::CopyThread, this, Source, Target);
    }
    else if (m_OperationType == OPERATION_MOVE)
    {
        DiskJobThread = std::thread(&MyClass::MoveThread, this, Source, Target);
    }
}

You were getting the error because *this resulted in trying to copy MyClass into the thread function, and the copy ctor of your class is deleted (because that of std::thread is deleted). However, the member functions CopyThread and MoveThread require a pointer as the first (hidden) argument anyway.

Live demonstration

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • +1, yes, I did not notice the fact that he is passing `*this` instead of `this`, you're right ;| – user123 Aug 22 '13 at 09:23
  • 1
    The '=' operator doesn't allow assigning a new thread object to `DiskJobThread` variable. I get this error: `error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::thread' (or there is no acceptable conversion)` – hkBattousai Aug 22 '13 at 09:31
  • @hkBattousai There is no `const std::thread` in the code you posted (and you can see on ideone that my change works). Post your actual code. – Angew is no longer proud of SO Aug 22 '13 at 09:34
  • I *think* you could use the slightly more obscure method of passing `std::ref(*this)`, but that would just very silly. – Kerrek SB Aug 22 '13 at 10:05
12

You can't initialize the thread object after it's created; by definition, initialization occurs when an object is created. But you can use swap to move a thread object into another:

std::thread thr1; // no thread of execution
std::thread thr2(my_function_object); // creates thread of execution
thr1.swap(thr2);  // thr1 is now running the thread created as thr2
                  // and thr2 has no thread of execution
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • It doesn't work. throws this error: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xmemory0(611): error C2280: 'std::thread::thread(const std::thread &)' : attempting to reference a deleted function 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\thread(70) : see declaration of 'std::thread::thread' 1> This diagnostic occurred in the compiler generated function 'Abc::Abc(const Abc &) – codeLover Jul 07 '17 at 05:43
  • @codeLover -- the problem is in your class `Abc` which apparently has a `std::thread` as a data member.. `std::thread` is not copyable, so your class has to either disable copy construction or have a copy constructor that somehow makes sense out of trying to copy that thread object. – Pete Becker Jul 07 '17 at 11:50
2

My aim is to keep an std::thread object as data member, and initialize it when needed.

Since a default-constructed std::thread object doesn't have an associated thread of execution, you can achieve that by using such an object as the target for a (move) assignment operation. However, note that the following is not initialization, but assignment:

std::thread th; // no thread of execution associated with th object
// ...
th = std::thread(func);

The temporary std::thread object created with std::thread(func) has an associated thread of execution. The ownership of this thread of execution is transferred to th through the move assignment – i.e., th steals the ownership of that thread of execution from the temporary.

Note that if th had an associated thread of execution at the moment of the assignment, std::terminate() would have been called.

JFMR
  • 23,265
  • 4
  • 52
  • 76