我有自己的线程实现,允许我管理与线程通信的各种方式。它基于C ++ std::thread
类。
我创建了一个线程来运行一个名为run()
的函数来调用用户函数。我希望能够做的是调用用户函数,包括我的线程对象指针。
我在构造函数中遇到问题。我想传递构造函数中指定的Args
并将this
添加到该列表中:
class safe_thread
{
public:
typedef std::shared_ptr<safe_thread> pointer_t;
typedef std::vector<pointer_t> vector_t;
template<class Function, class... Args>
safe_thread(Function&& f, Args&&... args)
: f_function([this, f, args...]{ std::bind(f, this, args...)(); })
{
[...snip...]
private:
std::function<void()> f_function;
};
// I use that constructor with two different types of signatures:
//
// 1. member function
//
safe_thread listen(&my_other_class::some_function, this);
// 2. static function
//
safe_thread worker(&my_static_function);
std::bind()
无法理解我当前的语法。它需要一个函数(f
)和args...
。那么,我如何更改 args...
以包含this
?
答案 0 :(得分:0)
对于那些感兴趣的人,我实际上找到了一个解决方案,即将this
参数移动到列表的末尾。这样我的成员函数std::invoke()
或标准静态函数的工作方式相同:
f_function([this, f, args...]{ std::invoke(f, args..., this)(); })
如果没有这个,正如Miles Budnek所提到的那样,需要一大堆SFINAE才能知道f
是成员函数还是普通函数。
问题在于,在某些情况下,我会在成员函数中创建一个线程,而在其他情况下,我会创建一个静态函数的步骤。
如果是成员函数,args...
列表中的第一个参数是调用者的this
,显然this
不能作为下一个参数移动。
实际上,我的第一次尝试产生了这个:
std::invoke(
&my_other_class::some_func, // correct
safe_thread::this, // wrong 'this' (i.e. safe_thread)
my_other_class::this, // 'this' for function, wrong location
...); // other parameters
通过更改通话中的顺序,我现在可以在正确的位置找到正确的'this':
std::invoke(
&my_other_class::some_func, // correct
my_other_class::this, // 'this' for function, correct location
..., // other parameters
safe_thread::this); // 'this' to safe_thread
显然这意味着我必须修复被调用函数的签名,但我对此很好。
如tkausl所述,std::bind()
不是必需的。有关详细信息,请参阅注释。话虽如此,std::invoke()
是C ++ 17,所以如果你仍然使用旧版本,你可能必须坚持使用std::bind()
。