error: use of deleted function ‘std::thread::thread(const std::thread&)'

user3723779 picture user3723779 · Dec 14, 2014 · Viewed 9.2k times · Source

The code bellow compiles and works as expected. The structure (class) A derives from std::thread and expands with an int more. The main code creates some threads and afterwards waits them to finish.

The problem is that while the code compiles without a destructor in struct A, when the destructor is uncommented ( ~A(){} ) I get:

error: use of deleted function ‘std::thread::thread(const std::thread&)'

and I have no idea on why.

Moreover I don't understand why the code works both with push_back and with emplace_back while according to what I understand it shouldn't work with push_back.

#include <iostream>
#include <thread>
#include <vector>

struct A : std::thread {
    int i;
    A(void f(const char*),const char* s,int i_) : std::thread{f,s},i{i_}{
        std::cout<<"A created"<<std::endl;
    }
    //~A(){} // uncomment to see error
};

void dosomething(const char* s){
    std::cout<<s<<std::endl;
}

int main(){
    std::vector<A> aa;
    aa.emplace_back(&dosomething,"hi people",3434);
    aa.push_back(A(&dosomething,"hi again people",777));
    aa.emplace_back(&dosomething,"hi again people",777);
    aa.push_back(A(&dosomething,"hi again people",777));

    for(auto& i:aa) i.join();
}

Answer

Useless picture Useless · Dec 14, 2014

If you want the destructor, you can fix your code by adding

A(A &&) = default;

to reinstate the implicit move ctor.


Your first problem is that adding the user-defined destructor disables implicit generation of the move constructor.

The (misleading) error you see is the STL trying to fall back on copying types that can't be moved, and failing because std::thread is deliberately non-copyable.

See this cppreference page's section on the Implicitly-declared move constructor, and this other question for some related motivation.

The second source of confusion is that push_back has a move overload, so your original code was never copying in the first place, only moving. If you want to prove this, comment the dtor back out so it works again, and then try to push_back a const reference to an A. It will complain about the copy constructor, meaning the other push_back calls aren't using that.