1

I am attempting to create a thread that calls a member function, from inside that same class.

I've seen some similar questions like this one.

However, mine does not seem to work. I get an error,

std::thread arguments must be invocable after conversion to rvalues

With this setup:

void MyClass::myFuncThread(
    const unsigned threadID,
    std::vector<myType*>& column,
    const unsigned x,
    const unsigned y) const
{
  // Each thread will write once to a single unique index of the vector, which should work 
  // despite vector not being thread-safe
  // Contents of this function omitted since it's not relevant to the error
}

void MyClass::myFunc(
    std::vector<myType*>& column,
    const unsigned x,
    const unsigned y) const
{
  column.resize(x, y);

  // Create a vector of threads
  std::vector<std::thread> threads;
  threads.resize(numThreads);

  // Launch all threads
  for (unsigned i = 0; i < threads.size(); i++)
    {
      threads[i] = std::thread(
          &MyClass::myFuncThread, this, i, column,
          x, y);
    }

  // Join all the threads
  for (unsigned i = 0; i < threads.size(); i++)
    {
      threads[i].join();
    }
}

Here is the full error when I attempt to compile: (edited with generic names)

/usr/include/c++/8/thread: In instantiation of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (MyClass*)(unsigned int, std::vector<myType*>&, unsigned int, unsigned int) const; _Args = {const MyClass*, unsigned int&, std::vector<myType*>&, const unsigned int&, const unsigned int&}; <template-parameter-1-3> = void]’:
myclass.cpp:100:23:   required from here
/usr/include/c++/8/thread:120:17: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
  static_assert( __is_invocable<typename decay<_Callable>::type,
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           typename decay<_Args>::type...>::value,
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It could be reference related. Only the std::vector needs to be a reference, the rest can just be copies. Do I still need to wrap something in std::ref() for some reason? Or do I just have the syntax of std::thread wrong somehow? Do I need to be using "future" and async, for some reason?

William Miller
  • 9,839
  • 3
  • 25
  • 46
Tyler Shellberg
  • 1,086
  • 11
  • 28

1 Answers1

4

It could be reference related.

It is.

Do I still need to wrap something in std::ref() for some reason?

Yes. You need to use std::ref.

Around what?

Around column.

wouldn't wrapping it with std::ref make it a reference to a reference?

There can't be a std::reference_wrapper of a reference. The template will be deduced as std::reference_wrapper<std::vector<myType*>> which is what you need.

It works! Could I ask, why exactly is the std::ref() needed there?

Because, as per documentation, std::thread makes copies of its arguments (by moving if possible). In case of columns which is an lvalue to a vector, a copy of the vector will be made. The copies are passed into the function as rvalues. Non-const lvalue references (such as the argument is) do not bind to rvalues, so the program is ill-formed.

what is it accomplishing?

std::reference_wrapper is a copyable object that maintains indirection internally and implicitly converts to lvalue of the referred type, which allows it to be passed into reference arguments. Instead of making a copy of the referred vector, the thread will make a copy of the reference wrapper.

what did you mean by the template being deduced here?

std::ref is a function template. You don't need to explicitly specify its template argument because it can be deduced from the argument that is passed into it.

P.S. Be very careful to keep the referred vector alive at least as long as the thread runs.

eerorika
  • 232,697
  • 12
  • 197
  • 326