所以我正在尝试创建一个类,其中包含一个用于不同类型仿函数的容器。
这是它的简化版本。
template<class T>
class Container
{
public:
template<typename F, typename ... ARGS>
void addTask(F && func, ARGS && ... args);
private:
std::deque<std::function<T()>> container;
//.....
};
template<class T>
template<typename F, typename ... ARGS>
T Container<T>::addTask(F && func, ARGS && ... args);
{
container.emplace_back(std::bind(f,args...));
//.....
}
还有一些问题我还没有解决。
- 有没有办法删除
std::bind
并存储不同的对象或指针? - 这可以更通用吗?我能否以某种方式将返回不同对象的函数存储在单个容器中(
int
,void
...)? - 创建任务的一些逻辑可以在编译时执行吗?比如
consexpr
bind。
最佳答案
来自 OP 的评论。
There are. This is simplified. I'm using futures and a special container in the real code. It is meant to be used in a multithreading environment
这叫做埋葬lede。
如果您正在存储要在其他线程中调用的可调用对象,则在其他线程中您需要签名 void()
.在这个线程中你想要一个std::future
被填充。
至于绑定(bind)参数,同时有多个 std
函数为你做这件事,我发现最好要求带有预绑定(bind)参数的可调用对象。他们可以在外面做,使用std::bind
或 lambda 或他们选择的任何其他方式。
所以这就来了
template<class Func,
class R = std::decay_t<std::result_of_t<Func const&()>>
>
std::future< R >
addTask( Func&& func ) {
auto task = std::packaged_task<R()>(std::forward<Func>(func));
auto ret = task.get_future();
container.push_back( std::packaged_task<void()>( std::move(task) ) );
return ret;
}
std::deque< std::packaged_task<void()> > container;
加入一些互斥量并摇晃和烘烤。
这里我使用std::packaged_task<void()>
作为具有该签名的任何内容的预写移动类型删除容器。我们不使用 future
它可以产生,这是一种浪费,但它比编写自己的仅移动调用一次拥有函数对象要短。
我个人刚刚给自己写了一个轻量级的 move-only std::function<void()>
esque 类而不是使用 std::packaged_task<void()>
,但这可能是不明智的。
从 addTask
返回的 future 当 packaged_task<R()>
时被填满被调用,它在 packaged_task<void()>
时被调用被调用(可能在另一个线程中)。
在结构之外,调用者可以给你任何零参数的可调用对象。
100 次中有 99 次,一个简单的 [some_arg]{ some_code; }
甚至 []{ some_code; }
作品。在复杂的情况下,他们可以乱用 std::bind
或使用更复杂的 lambda 的 C++14 改进。
将参数存储到 addTask
中将线程任务队列的职责与参数混为一谈。
事实上,我会独立于我的线程池编写一个线程安全队列,并让线程池使用它:
template<class T>
struct thread_safe_queue;
struct thread_pool {
thread_safe_queue< std::packaged_task<void()> > queue;
// etc
};
在 C++17 中,您的绑定(bind)的替代品如下所示:
[
func = std::forward<Func>(func),
args = std::make_tuple( std::forward<Args>(args)... )
]() mutable {
std::apply( func, std::move(args) );
}
在 C++14 中你可以写 notstd::apply
相当容易。 Move-into-lambda 需要 C++14,因此如果您需要有效地移动参数,您需要 std bind 或 C++11 中的手动函数对象。
我会争辩说,将参数强绑定(bind)放在代码域中使用线程池是最好的。
这也允许线程池做一些事情,比如传递任务可选的额外参数,比如“取消标记”等等。
关于c++ - 现代 C++ 中 std::bind 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46118564/