There are a few problems here:
std::thread itself is moveable, so wrapping it in a std::unique_ptr may be entirely unnecessary
std::thread copies any function arguments to the background thread.
- You need to pass the arguments to the functor to be called on the background thread to the constructor of
std::thread; any parameters of the function to be called in the thread that are parameters of a refercence type that should reference the objects on the thread creating the thread need to be wrapped in std::reference_wrapper to be passed to the background thread without a copy of the object being created.
- There are a few other syntax errors in the function.
In this scenario you likely shouldn't be using perfect forwarding, but simply provide functionality for choosing the appropriate conversion for the parameter to be passed to the thread. Here's a modification of your code that should accomplish what you're trying to do:
// helper type for passing everything other than lvalue references by value
template<class T>
struct ThreadPassType
{
using value_type = T&&;
};
template<class T>
struct ThreadPassType<T&>
{
using value_type = std::reference_wrapper<T>;
};
struct MyClass
{
std::thread td_;
template<typename C, typename F, typename... Args>
void run(F (C::*f)(Args...), std::type_identity_t<C>* c, std::type_identity_t<Args>... args)
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
// note: we're excluding anything from deducing the template parameters except for the member function pointer
{
td_ = std::thread(
f, // std::thread deals with member function pointers on its own
c,
static_cast<ThreadPassType<Args>::value_type>(args)...);
}
};
struct TestType
{
void print(std::string const& s)
{
std::cout << static_cast<void const*>(&s) << ": " << s << '\n';
}
void test(std::string s0, std::string& s1, std::string const& s2, std::string&& s3)
{
std::cout << "background thread:\n";
print(s0);
print(s1);
print(s2);
print(s3);
}
};
int main()
{
TestType t;
std::string s0 = "a";
std::string s1 = "b";
std::string s2 = "c";
std::string s3 = "d";
std::cout << "main thread(before):\n";
t.print(s0);
t.print(s1);
t.print(s2);
t.print(s3);
MyClass mc;
mc.run(&TestType::test, &t, s0, s1, s2, std::move(s3));
// deduced template parameters: <TestType, void, std::string, std::string&, std::string const&, std::string&&>
mc.td_.join();
std::cout << "main thread(after):\n";
t.print(s0);
t.print(s1);
t.print(s2);
t.print(s3);
}
template<typename fn_t> run(fn_t fn) { td_ = std::make_unique<std_thread>(f); }and let the lambda expression do all that heavy lifting. You might then also use SFINAE (or concepts) to only accept functions without return statements and noexcept specification (because you can't return anything and you cannot handle exceptions anymore)std::thread...run’s parameters (including theargspack)?run's parameters as the parameters of the lambda. So I could callrunlike this:run(MyClass:Test, &myClassObj, param1, param2);