383

I am trying to construct a std::thread with a member function that takes no arguments and returns void. I can't figure out any syntax that works - the compiler complains no matter what. What is the correct way to implement spawn() so that it returns a std::thread that executes test()?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};
Barry
  • 286,269
  • 29
  • 621
  • 977
abergmeier
  • 13,224
  • 13
  • 64
  • 120
  • 1
    Do u mean the function returns void, called void or it just doesn't have any parameters. Can u add the code for what you are trying to do? – Zaid Amir May 20 '12 at 13:02
  • Have you tested? (I haven't yet.) Your code seems to rely on the RVO (return-value-optimzation), but I don't think you are supposed to do so. I think using `std::move( std::thread(func) );` is better, for `std::thread` doesn't have a copy-constructor. – RnMss Oct 10 '13 at 11:28
  • 4
    @RnMss: [you can rely on RVO](http://stackoverflow.com/a/17473869/1905448), using `std::move` is redundant in this case - were this not true, and there was no copy constructor, the compiler would give an error anyway. – Qualia Oct 22 '15 at 13:19

5 Answers5

485
#include <thread>
#include <iostream>

class bar {
public:
  void foo() {
    std::cout << "hello from member function" << std::endl;
  }
};

int main()
{
  std::thread t(&bar::foo, bar());
  t.join();
}

EDIT: Accounting your edit, you have to do it like this:

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

UPDATE: I want to explain some more points, some of them have also been discussed in the comments.

The syntax described above is defined in terms of the INVOKE definition (§20.8.2.1):

Define INVOKE (f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t 1 is an object of type T or a
    reference to an object of type T or a reference to an object of a
    type derived from T;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t 1 is not one of the types described in the previous item;
  • f(t1, t2, ..., tN) in all other cases.

Another general fact which I want to point out is that by default the thread constructor will copy all arguments passed to it. The reason for this is that the arguments may need to outlive the calling thread, copying the arguments guarantees that. Instead, if you want to really pass a reference, you can use a std::reference_wrapper created by std::ref.

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

By doing this, you are promising that you will take care of guaranteeing that the arguments will still exist when the thread operates on them.


Note that all the things mentioned above can also be applied to std::async and std::bind.

Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107
  • 1
    At least this way it compiles. Though I have no idea why you are passing the instance as the second argument. – abergmeier May 20 '12 at 13:37
  • 16
    @LCID: The multi-argument version of the `std::thread` constructor works as if the arguments were passed to `std::bind`. To call a member function, the first argument to `std::bind` must be a pointer, reference, or shared pointer to an object of the appropriate type. – Dave S May 20 '12 at 13:49
  • Where do you take it from that the constructor acts like an implicit `bind`? I can't find that anywhere. – Kerrek SB May 20 '12 at 13:58
  • @DaveS this would explain a lot. Though I have the same problem as @Kerrek SB, I cannot find any documentation regarding member functions with `std::thread` and/or `std::bind`. – abergmeier May 20 '12 at 14:14
  • 3
    @KerrekSB, compare [thread.thread.constr]p4 with [func.bind.bind]p3, the semantics are quite similar, defined in terms of the INVOKE pseudocode, which defines how member functions are called – Jonathan Wakely May 20 '12 at 14:39
  • 4
    remember that not static member functions as first parameter take instance of class (it's not visible for programmer), so when passing this method as raw function you will always encounter a problem during compilation and declaration mismatch. – zoska Oct 10 '13 at 11:56
  • `std::thread` cannot be used with `std::ref` like this. Your last example is wrong. See http://stackoverflow.com/questions/21059115/c11-thread-class-how-to-use-a-class-member-function. – juanchopanza Jul 20 '14 at 08:41
  • @juanchopanza, it's not clear whether `arg1` in the last example is the "this" parameter or just some argument to a non-member function ... `foo` may not be intended to be something like `&bar::foo` ... in which case `ref` is fine – Jonathan Wakely Oct 09 '14 at 10:28
  • Just to inform there is a proposal for an invoke function and is available for c++11 at (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3727.html) and (https://github.com/tomaszkam/proposals/blob/master/invoke/invoke_cpp11.hpp) – Paulo Neves Jan 07 '15 at 01:53
  • I tried the bar() type, it doesn't work. Then I saw your edit, with this it works. This is very helpful since boost::thread doesn't need the this and std::thread requires it. – Splash Nov 05 '15 at 17:45
  • @juanchopanza a bit of a late replay but yeah, that's supposed to be a general example not a member function one. – Stephan Dollberg Dec 23 '18 at 14:37
132

Since you are using C++11, lambda-expression is a nice&clean solution.

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

since this-> can be omitted, it could be shorten to:

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

or just (deprecated)

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

RnMss
  • 3,696
  • 3
  • 28
  • 37
  • 9
    In general, you shouldn't use `std::move` when returning a local variable by value. This actually inhibits RVO. If you just return by value (without the move) the compiler may use RVO, and if it doesn't the standard says it has to invoke move semantics. – zmb Oct 10 '13 at 11:53
  • @zmb, with the exception that you want code to compile on VC10, you have to move if the return type is not CopyConstructable. – abergmeier Oct 10 '13 at 13:20
  • @zmb `This actually inhibits RVO.` Yes it does. But that is a hack, and RVO is a hack, too. It was invented for the pre-C++11 days, where people hadn't got a better idea. And then RValue-Ref came to rescue. So, I'd suggest keep using move-constructor and wait for someday that compilers use RVO for move-constructors only, instead of the copy ones. : ) – RnMss Oct 10 '13 at 18:13
  • 7
    RVO still generates better code than move semantics, and is not going away. – Jonathan Wakely Oct 09 '14 at 10:41
  • minus 1 for explicit `std::move`. Let me know if you fix it, and I'll revert the downvote. – Yakk - Adam Nevraumont Aug 19 '15 at 21:34
  • @Yakk it's following the "EDIT (Nov 24, 2014)" – RnMss Aug 20 '15 at 00:38
  • @RnMss: You would not even need std::move if you created the thread as a local variable (non temporary). So your statement in the edit from 2014 is still (partially) incorrect – MikeMB Aug 20 '15 at 01:18
  • 3
    Be careful with `[=]`. With that you can inadvertently copy a huge object. In general, it's a *code smell* to use `[&]` or `[=]`. – rustyx Sep 09 '16 at 08:37
  • I think it would be a better idea to use `[&]`, no? – Everyone Mar 17 '17 at 08:06
  • 3
    @Everyone Don't forget it's a thread here. This means the lambda function may outlive its context scope. So by using capturing-by-reference (`[&]`), you may introduce bugs like some dangling references. (For example, `std::thread spawn() { int i = 10; return std::thread( [&] { std::cout< – RnMss Jan 09 '18 at 08:13
  • RnMss, while thats quite true, I believe cases where this is an issue should be redesigned anyway because it's calling a thread on a member function so the thread better close as the object goes out of scope. At least that's what i always like to follow. – Everyone Jan 09 '18 at 08:25
35

Here is a complete example

#include <thread>
#include <iostream>

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;
}

Compiling with g++ produces the following result

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
  • 367
  • 3
  • 2
31

@hop5 and @RnMss suggested to use C++11 lambdas, but if you deal with pointers, you can use them directly:

#include <thread>
#include <iostream>

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;
}

outputs

2

Rewritten sample from this answer would be then:

#include <thread>
#include <iostream>

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;
}
Andrey Starodubtsev
  • 5,139
  • 3
  • 32
  • 46
1

Some users have already given their answer and explained it very well.

I would like to add few more things related to thread.

  1. How to work with functor and thread. Please refer to below example.

  2. The thread will make its own copy of the object while passing the object.

    #include<thread>
    #include<Windows.h>
    #include<iostream>
    
    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 
                       //i.e. 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();
    }
    

Another way of achieving the same thing is like:

void main()
{
    thread t((CB()));

    t.join();
}

But if you want to pass the object by reference then use the below syntax:

void main()
{
    CB obj;
    //thread t(obj);
    thread t(std::ref(obj));
    t.join();
}
Mohit
  • 885
  • 9
  • 7
  • Hi, could you explain how it's possible to create a thread from a member function without creating an object ? in case `thread t((CB()));` not `CB` object is created ? Can you answer my question here please https://stackoverflow.com/q/71152949/7264131 – Sad egg Feb 17 '22 at 05:56