5

I'm trying to make a form of std::thread that puts a wrapper around the code executed in the thread. Unfortunately I can't get it to compile due likely to my poor understanding of rvalues and the Function templated type I'm trying to pass. Here's my code:

#include <vector>
#include <thread>
#include <utility>

void Simple2(int a, int b) {}

template <typename Function, typename... Args>
void Wrapper(Function&& f, Args&&... a) {
  f(std::forward<Args>(a)...);
}

class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<Function, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};

int main() {
  Wrapper(Simple2, 3, 4);       // Works

  Pool pool;
  pool.Binder(Simple2, 3, 4);   // Doesn't compile
}

The Clang3.0 output that seems important here is:

/usr/include/c++/4.6/functional:1286:9: error: non-const lvalue reference to type 'void (int, int)' cannot bind to a value of unrelated type 'void (*)(int, int)'

and

note: in instantiation of function template specialization 'std::thread::thread<void (void (&)(int, int), int &&, int &&), void (&)(int, int), int, int>' requested here

Which I think is hinting at a mismatch between Wrapper<Function, Args...> and the rvalues f, a... being given to std::thread.

Curiously this compiles in GCC4.9 and newer Clang if I change the std::forward<Function>(f) to std::ref(f).

zmoratto
  • 73
  • 1
  • 5

1 Answers1

6

This is one of those rare cases where the difference between passing a function and passing a function pointer makes a difference. If you do:

pool.Binder(&Simple2, 3, 4);  

it should work. Or alternatively you can have Binder decay its argument to a function pointer:

class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<typename std::decay<Function>::type, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};

which in C++14 simplifies to:

class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<std::decay_t<Function>, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • This fixed things for g++4.7+. Unfortunately an error still persists in gcc4.6 of `invalid initialization of reference of type 'void (*&&)(int, int)' from expression of type 'void (*)(int, int)'`. Possibly this is a problem now with the stdlib that ships with gcc4.6. – zmoratto Nov 20 '14 at 17:52
  • You are a life saver. A whole day lost to this problem and I finally found the solution. Can you elaborate on why passing the function pointer makes the difference? Thousand thank yous. – Paulo Neves Jan 06 '15 at 21:33
  • 1
    @PauloNeves: Glad I could help. When binding to a reference parameter, there is no "decay". When binding to by-value parameters, some types decay: functions arguments decay to pointers to functions; array arguments decay to pointers to array-element-types; top-level cv-qualifiers are discarded in a decay. Function types are funny (in a bad way), you don't want to deal with them. So it is usually best to decay function types to function pointer types. I forget all of the details for why function types are difficult to deal with. I simply got shocked and learned not to touch the hot wire. – Howard Hinnant Jan 06 '15 at 22:43