Iniciar o encadeamento com a function de membro

Eu estou tentando construir um std::thread com uma function de membro que não leva argumentos e retorna void . Eu não consigo descobrir nenhuma syntax que funcione – o compilador reclama, não importa o quê. Qual é a maneira correta de implementar o spawn() para que ele retorne um std::thread que execute test() ?

 #include  class blub { void test() { } public: std::thread spawn() { return { test }; } }; 

 #include  #include  class bar { public: void foo() { std::cout < < "hello from member function" << std::endl; } }; int main() { std::thread t(&bar::foo, bar()); t.join(); } 

EDIT: Contabilizando sua edição, você tem que fazer assim:

  std::thread spawn() { return std::thread(&blub::test, this); } 

UPDATE: Eu quero explicar mais alguns pontos, alguns deles também foram discutidos nos comentários.

A syntax descrita acima é definida em termos da definição INVOKE (§20.8.2.1):

Defina INVOKE (f, t1, t2, ..., tN) da seguinte maneira:

  • (t1. * f) (t2, ..., tN) quando f é um ponteiro para uma function-membro de uma class T e t1 é um object do tipo T ou uma referência a um object do tipo T ou uma referência a um object de um tipo derivado de T;
  • ((* t1). * f) (t2, ..., tN) quando f é um ponteiro para uma function-membro de uma class T e t1 não é um dos tipos descritos no item anterior;
  • t1. * f quando N == 1 e f é um ponteiro para dados de membro de uma class T e t 1 é um object do tipo T ou um
    referência a um object do tipo T ou uma referência a um object de
    tipo derivado de T;
  • (* t1). * f quando N == 1 e f é um apontador para dados de membro de uma class T e t 1 não é um dos tipos descritos no item anterior;
  • f (t1, t2, ..., tN) em todos os outros casos.

Outro fato geral que quero ressaltar é que, por padrão, o construtor de encadeamento copia todos os argumentos passados ​​para ele. A razão para isso é que os argumentos podem precisar sobreviver o segmento de chamada, copiando os argumentos garante isso. Em vez disso, se você quiser realmente passar uma referência, você pode usar um std::reference_wrapper criado por std::ref .

 std::thread (foo, std::ref(arg1)); 

Ao fazer isso, você está prometendo que cuidará de garantir que os argumentos ainda existirão quando o encadeamento operar neles.


Observe que todas as coisas mencionadas acima também podem ser aplicadas a std::async e std::bind .

Como você está usando o C ++ 11, a expressão lambda é uma solução agradável e limpa.

 class blub { void test() {} public: std::thread spawn() { return std::thread( [this] { this->test(); } ); } }; 

Como this-> pode ser omitido, pode ser reduzido para:

 std::thread( [this] { test(); } ) 

ou apenas

 std::thread( [=] { test(); } ) 

Aqui está um exemplo completo

 #include  #include  class Wrapper { public: void member1() { std::cout < < "i am member1" << std::endl; } void member2(const char *arg1, unsigned arg2) { std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl; } std::thread member1Thread() { return std::thread([=] { member1(); }); } std::thread member2Thread(const char *arg1, unsigned arg2) { return std::thread([=] { member2(arg1, arg2); }); } }; int main(int argc, char **argv) { Wrapper *w = new Wrapper(); std::thread tw1 = w->member1Thread(); std::thread tw2 = w->member2Thread("hello", 100); tw1.join(); tw2.join(); return 0; } 

Compilando com g ++ produz o seguinte resultado

 g++ -Wall -std=c++11 hello.cc -o hello -pthread i am member1 i am member2 and my first arg is (hello) and second arg is (100) 

@ hop5 e @RnMss sugeriram usar lambdas C ++ 11, mas se você lidar com pointers, você pode usá-los diretamente:

 #include  #include  class CFoo { public: int m_i = 0; void bar() { ++m_i; } }; int main() { CFoo foo; std::thread t1(&CFoo::bar, &foo); t1.join(); std::thread t2(&CFoo::bar, &foo); t2.join(); std::cout < < foo.m_i << std::endl; return 0; } 

saídas

 2 

Amostra reescrita desta resposta seria então:

 #include  #include  class Wrapper { public: void member1() { std::cout < < "i am member1" << std::endl; } void member2(const char *arg1, unsigned arg2) { std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl; } std::thread member1Thread() { return std::thread(&Wrapper::member1, this); } std::thread member2Thread(const char *arg1, unsigned arg2) { return std::thread(&Wrapper::member2, this, arg1, arg2); } }; int main() { Wrapper *w = new Wrapper(); std::thread tw1 = w->member1Thread(); tw1.join(); std::thread tw2 = w->member2Thread("hello", 100); tw2.join(); return 0; } 

Alguns usuários já deram sua resposta e explicaram isso muito bem.

Eu gostaria de adicionar mais algumas coisas relacionadas ao segmento.

  1. Como trabalhar com o functor e o thread. Por favor, consulte o exemplo abaixo.

  2. O encadeamento fará sua própria cópia do object enquanto passa o object.

     #include #include #include using namespace std; class CB { public: CB() { cout < < "this=" << this << endl; } void operator()(); }; void CB::operator()() { cout << "this=" << this << endl; for (int i = 0; i < 5; i++) { cout << "CB()=" << i << endl; Sleep(1000); } } void main() { CB obj; // please note the address of obj. thread t(obj); // here obj will be passed by value //ie thread will make it own local copy of it. // we can confirm it by matching the address of //object printed in the constructor // and address of the obj printed in the function t.join(); } 

Outra maneira de conseguir a mesma coisa é como:

 void main() { thread t((CB())); t.join(); } 

Mas se você quiser passar o object por referência, use a syntax abaixo:

 void main() { CB obj; //thread t(obj); thread t(std::ref(obj)); t.join(); }