1

I can't figure out how to pass member function with variadic template arguments to std::thread constructor. I have a method that receives function and its arguments and need to pass them to other method which is invoked in new thread and calls passed function there. Here is simplified version:

class Test { public: template<typename Function, typename... Args> void Run(Function&& f, Args&&... args) { std::thread t(&Test::Operation, this, f, args...); // how??? t.detach(); } template<typename Function, typename... Args> void Operation(Function&& f, Args&&... args) { f(args...); } }; Test test; test.Run([](const std::string& msg) { std::cout << msg; }, "Hi!"); 

There is something wrong in passing arguments this way, I get the following error: 'std::thread::thread': no overloaded function takes 4 arguments. How can I do this?

2
  • Why not just use function overloading? Commented Jan 29, 2016 at 23:10
  • @vonbrand I need to execute large number of various functions in similar scenario so I want to minimize code for this Commented Jan 29, 2016 at 23:25

1 Answer 1

7

The problem here is that when you pass &Test::Operation to the thread constructor, it can't deduce the template parameters for &Test::Operation. (If you want to understand why this deduction cannot be done, you should probably ask a separate question.) The point is that you need to explicitly specify the template arguments for &Test::Operation.

This will look like this:

template<typename Function, typename... Args> void Run(Function&& f, Args&&... args) { std::thread t(&Test::Operation<std::decay_t<Function>, std::decay_t<Args>...>, this, std::forward<Function>(f), std::forward<Args>(args)...); t.detach(); } 

I added perfect forwarding for you: it's important because you don't want to perform an unnecessary copy, right?

But why do we need the decay_t? It's because the thread constructor decays its arguments before storing copies of them in the new thread's internal storage, so, for example, if you pass in "Hi!", then the deduced type is const char (&)[4], but it will become a const char* once decayed, and the decay is irreversible, so Test::Operation must not be expecting a const char (&)[4], which cannot be initialized from the decayed const char*. So Test::Operation must be specified to take the decayed type.

You also probably want perfect forwarding during the actual invocation.

Link: http://coliru.stacked-crooked.com/a/c9d04c03a3758b51

Sign up to request clarification or add additional context in comments.

1 Comment

Definitely what I was looking for. Thanks for explanation!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.