Você está faltando instrumentar a cópia-construção e mover a construção. Uma simples modificação no seu programa fornecerá evidências de que as construções estão ocorrendo.
Copiar Construtor
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{t};
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Saída (endereços variam)
Constructed : 0x104055020
Copy constructed : 0x104055160 (source=0x104055020)
Copy constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104055020
Copiar Construtor e Mover Construtor
Se você fornecer um gerenciador, será preferido para pelo menos uma dessas cópias:
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{t};
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Saída (endereços variam)
Constructed : 0x104057020
Copy constructed : 0x104057160 (source=0x104057020)
Move constructed : 0x602000008a38 (source=0x104057160)
Destroyed : 0x104057160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104057020
Referência agrupada
Se você deseja evitar essas cópias, pode agrupar sua chamada em um wrapper de referência ( std::ref
). Como você deseja utilizar t
após a conclusão da peça de rosqueamento, isso é viável para a sua situação. Na prática, você deve ter muito cuidado ao encadear referências a objetos de chamada, pois a vida útil do objeto deve estender pelo menos o tempo que o encadeamento que utiliza a referência.
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{std::ref(t)}; // LOOK HERE
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Saída (endereços variam)
Constructed : 0x104057020
Thread is joining...
Thread running at : 11
x : 11
Destroyed : 0x104057020
Observe que, embora eu tenha mantido as sobrecargas de copy-ctor e move-ctor, nenhuma delas foi chamada, pois o wrapper de referência agora é a coisa que está sendo copiada / movida; não é a coisa que ele faz referência. Além disso, essa abordagem final fornece o que você provavelmente estava procurando; t.x
de volta main
é, de fato, modificado para 11
. Não estava nas tentativas anteriores. No entanto, não posso enfatizar isso o suficiente: tenha cuidado ao fazer isso . A vida útil do objeto é crítica .
Mover, e nada mais
Por fim, se você não tem interesse em reter t
como no seu exemplo, pode usar a semântica de movimentação para enviar a instância diretamente para o encadeamento, movendo-se ao longo do caminho.
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
thread t1{tFunc()}; // LOOK HERE
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
return 0;
}
Saída (endereços variam)
Constructed : 0x104055040
Move constructed : 0x104055160 (source=0x104055040)
Move constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Destroyed : 0x104055040
Thread is joining...
Thread running at : 11
Destroyed : 0x602000008a38
Aqui você pode ver que o objeto é criado, a referência rvalue para o mesmo e enviada diretamente para std::thread::thread()
, onde é movida novamente para seu local de descanso final, pertencente ao encadeamento daquele ponto em diante. Não há copiadores envolvidos. Os dtors reais são contra duas conchas e o objeto concreto de destino final.